home *** CD-ROM | disk | FTP | other *** search
Wrap
# Source Generated with Decompyle++ # File: in.pyc (Python 1.5) PACKAGE = 'PySol' PACKAGE_URL = 'http://pysol.tsx.org' import os import sys try: bundle except: bundle = 1 if os.name == 'nt': bundle = 1 | 2 if not hasattr(sys, 'platform'): sys.platform = 'unknown' if sys.platform[:4] != 'java': if sys.version[:5] < '1.5.2': print '%s needs Python 1.5.2 or better (you have %s)' % (PACKAGE, sys.version) sys.exit(2) try: import __builtin__ import glob import math import operator import re import string import time import types import StringIO import formatter import htmllib UserDict UserList if os.name == 'mac': pass except ImportError: ex = None try: import traceback traceback.print_exc() except: pass print '\n%s cannot find the standard Python libraries.\nPlease check your Python installation.\n' % PACKAGE sys.exit(2) try: UnpicklingError except ImportError: UnpicklingError except: Unpickler thread = None try: import thread except: Unpickler Pickler import pickle thread = None pysolsoundserver = None win32api = None if os.name == 'nt': try: import win32api except: import pickle if thread and not ('--nosound' in sys.argv[1:]) else Pickler win32api = None traceback = None try: import Tkinter import Canvas import tkColorChooser import tkFileDialog except ImportError: ex = None try: import traceback traceback.print_exc() except: pass print '\n%s cannot find the Tkinter Python libraries.\nPlease check your Python installation.\n' % PACKAGE sys.exit(2) TclError = Tkinter.TclError VERSION = '4.60' VERSION_DATE = '02 Aug 2000' VERSION_MAJOR = 4 VERSION_MINOR = 60 VERSION_TUPLE = (4, 60) def indices(object): return tuple(range(len(object))) def trange(start, stop = None, step = None): if stop is None: return tuple(range(start)) elif step is None: return tuple(range(start, stop)) else: return tuple(range(start, stop, step)) def range_len(object): return range(len(object)) def reverse(sequence): if type(sequence) is types.TupleType: l = list(sequence) l.reverse() l = tuple(l) elif type(sequence) is types.ListType: l = sequence[:] l.reverse() else: l = list(sequence) l.reverse() return l def irange(object, indices = None): pass def count(condition, sequence): if condition is None: return len(filter(None, sequence)) else: return len(filter(None, map(condition, sequence))) def exists(condition, sequence): if condition is None: condition = operator.truth for obj in sequence: pass return 0 def forall(condition, sequence): if condition is None: condition = operator.truth for obj in sequence: pass return 1 bool = operator.truth def sgn(expr): if expr < 0: return -1 if expr > 0: return 1 return 0 EnvError = (IOError, OSError, os.error) class SubclassResponsibility(Exception): pass def static(f, *args, **kw): if args: a = tuple([ f.im_class()] + list(args)) else: a = (f.im_class(),) return apply(f, a, kw) def ifelse(expr, val1, val2): if expr: return val1 return val2 def merge_dict(dict1, dict2, merge_none = 1): for k, v in dict2.items(): if dict1.has_key(k): if type(dict1[k]) is type(v): dict1[k] = v elif dict1[k] is None and merge_none: dict1[k] = v def latin1_to_ascii(n): n = re.sub('[\\xc4]', 'Ae', n) n = re.sub('[\\xd6]', 'Oe', n) n = re.sub('[\\xdc]', 'Ue', n) n = re.sub('[\\xe4]', 'ae', n) n = re.sub('[\\xf6]', 'oe', n) n = re.sub('[\\xfc]', 'ue', n) return n htmlentitydefs_i = { } def latin1_to_html(n): (s, g) = ('', htmlentitydefs_i.get) for c in n: s = s + g(c, c) return s def hexify(s): return '%02x' * len(s) % tuple(map(ord, s)) def getusername(): user = None if os.name == 'nt': try: user = string.strip(win32api.GetUserName()) except: pass if not user: user = string.strip(os.environ.get('USER', '')) if not user: user = string.strip(os.environ.get('LOGNAME', '')) return user def gethomedir(): default_home = os.curdir if os.name == 'nt': default_home = 'c:\\' home = string.strip(os.environ.get('HOME', '')) if not home or not os.path.isdir(home): if os.name == 'nt': home = os.environ.get('HOMEDRIVE', '') + os.environ.get('HOMEPATH', '') if not home or not os.path.isdir(home): home = default_home return os.path.abspath(home) def getprefdir(package, home = None): if os.name == 'mac': (vrefnum, dirid) = macfs.FindFolder(MACFS.kOnSystemDisk, MACFS.kPreferencesFolderType, 0) fss = macfs.FSSpec((vrefnum, dirid, ':' + package)) return fss.as_pathname() if os.name == 'nt' and win32api: try: windir = os.path.abspath(win32api.GetWindowsDirectory()) if windir and os.path.isdir(windir): user = string.strip(win32api.GetUserName()) return os.path.join(windir, 'Preferences', package, user) except: pass if home is None: home = gethomedir() return os.path.join(home, '.' + string.lower(package)) uclock = time.clock usleep = time.sleep if os.name == 'posix': uclock = time.time def destruct(obj): pass class Struct: def __init__(_, **kw): _.__dict__.update(kw) def __str__(_): return str(_.__dict__) def addattr(_, **kw): for key in kw.keys(): pass _.__dict__.update(kw) def update(_, dict): for key in dict.keys(): pass _.__dict__.update(dict) def clear(_): for key in _.__dict__.keys(): t = type(key) if t is types.ListType: _.__dict__[key] = [] elif t is types.TupleType: _.__dict__[key] = () elif t is types.DictType: _.__dict__[key] = { } else: _.__dict__[key] = None def copy(_): c = Struct() c.__class__ = _.__class__ c.__dict__.update(_.__dict__) return c def kwdefault(kw, **defaults): for k, v in defaults.items(): pass class KwStruct: def __init__(_, kw = { }, **defaults): if isinstance(kw, KwStruct): kw = kw.__dict__ if isinstance(defaults, KwStruct): defaults = defaults.__dict__ if defaults: kw = kw.copy() for k, v in defaults.items(): pass _.__dict__.update(kw) def __setattr__(_, key, value): if not _.__dict__.has_key(key): raise AttributeError, key _.__dict__[key] = value def __getitem__(_, key): return getattr(_, key) def get(_, key, default = None): return _.__dict__.get(key, default) def getKw(_): return _.__dict__ def pickle(obj, filename, binmode = 0): f = None try: f = open(filename, 'wb') p = Pickler(f, binmode) p.dump(obj) f.close() f = None finally: if f: f.close() def unpickle(filename): (f, obj) = (None, None) try: f = open(filename, 'rb') p = Unpickler(f) x = p.load() f.close() f = None obj = x finally: if f: f.close() return obj def spawnv(file, args = ()): if not args: args = () args = (file,) + tuple(args) if not os.path.isfile(file): raise os.error, str(file) mode = os.stat(file)[0] if not (mode & 64): return 0 if os.name == 'nt': os.spawnv(os.P_DETACH, file, args) return 1 elif os.name == 'posix': pid = os.fork() if pid == -1: raise os.error, 'fork failed' if pid != 0: try: os.waitpid(pid, 0) except: pass return 1 for fd in range(255, -1, -1): try: os.close(fd) except: 0 range(255, -1, -1) try: fd = os.open('/dev/null', os.O_RDWR) os.dup(fd) os.dup(fd) except: 0 range(255, -1, -1) try: if os.fork() == 0: try: os.setpgrp() except: 0 range(255, -1, -1) os.execv(file, args) except: 0 range(255, -1, -1) while 1: os._exit(0) continue 0 return 0 def spawnvp(file, args = ()): if file and os.path.isabs(file): try: if spawnv(file, args): return file except: pass return None path = os.environ.get('PATH', '') path = string.splitfields(path, os.pathsep) for dir in path: try: if dir and os.path.isdir(dir): f = os.path.join(dir, file) try: if spawnv(f, args): return f except: 0 path except: 0 path return None def openURL(url): if os.name == 'nt': SW_HIDE = 0 SW_SHOWNORMAL = 1 SW_SHOW = 5 try: handle = win32api.ShellExecute(0, 'open', url, None, '.', SW_SHOW) return 1 except: pass return 0 class PysolRandom: MAX_SEED = 0x0L ORIGIN_UNKNOWN = 0 ORIGIN_RANDOM = 1 ORIGIN_PREVIEW = 2 ORIGIN_SELECTED = 3 ORIGIN_NEXT_GAME = 4 def __init__(_, seed = None): if seed is None: seed = _._getRandomSeed() _.initial_seed = _.setSeed(seed) _.origin = _.ORIGIN_UNKNOWN def __str__(_): return _.str(_.initial_seed) def reset(_): _.seed = _.initial_seed def getSeed(_): return _.seed def setSeed(_, seed): seed = _._convertSeed(seed) if type(seed) is not types.LongType: raise TypeError, 'seeds must be longs' if not None if seed <= seed else seed <= _.MAX_SEED: raise ValueError, 'seed out of range' _.seed = seed return seed def copy(_): random = PysolRandom(0x0L) random.__class__ = _.__class__ random.__dict__.update(_.__dict__) return random def choice(_, seq): return seq[int(_.random() * len(seq))] def randint(_, a, b): return a + int(_.random() * (b + 1 - a)) def random(_): raise SubclassResponsibility def _convertSeed(_, seed): return long(seed) def increaseSeed(_, seed): if seed < _.MAX_SEED: return seed + 0x1L return 0x0L def _getRandomSeed(_): t = long(time.time() * 256.0) t = (t ^ t >> 24) % (_.MAX_SEED + 0x1L) return t def shuffle(_, seq): n = len(seq) - 1 while n > 0: j = _.randint(0, n) (seq[n], seq[j]) = (seq[j], seq[n]) n = n - 1 class LCRandom64(PysolRandom): MAX_SEED = 0xFFFFFFFFFFFFFFFFL def str(_, seed): s = repr(long(seed))[:-1] s = '0' * (20 - len(s)) + s return s def random(_): _.seed = _.seed * 0x5851F42D4C957F2DL + 0x1L & _.MAX_SEED return (_.seed >> 21 & 0x7FFFFFFFL) / 2147483648.0 class LCRandom31(PysolRandom): MAX_SEED = 0x7FFFFFFFL def str(_, seed): return '%05d' % int(seed) def random(_): _.seed = _.seed * 0x343FDL + 0x269EC3L & _.MAX_SEED return (_.seed >> 16) / 32768.0 def randint(_, a, b): _.seed = _.seed * 0x343FDL + 0x269EC3L & _.MAX_SEED return a + int(_.seed >> 16) % (b + 1 - a) class WHRandom(PysolRandom): MAX_SEED = 0x763CL * 0x7662L * 0x7672L - 1 def str(_, seed): (x, y, z) = _._unpackSeed(seed) return '%04x-%04x-%04x' % (x, y, z) def random(_): (x, y, z) = _._unpackSeed(_.seed) x = 171 * x % 30269 y = 172 * y % 30307 z = 170 * z % 30323 _.seed = _._packSeed(x, y, z) return (x / 30269.0 + y / 30307.0 + z / 30323.0) % 1.0 def _convertSeed(_, seed): if type(seed) is types.TupleType: return _._packSeed(seed[0], seed[1], seed[2]) return long(seed) def _packSeed(_, x, y, z): if __debug__: if x < x: pass elif not x < 30269: raise AssertionError if __debug__: if y < y: pass elif not y < 30307: raise AssertionError if __debug__: if z < z: pass elif not z < 30323: raise AssertionError seed = ((x - 1) * 0x7662L + (y - 1)) * 0x7672L + (z - 1) if __debug__: if seed <= seed: pass elif not seed <= _.MAX_SEED: raise AssertionError return seed def _unpackSeed(_, seed): if __debug__: if seed <= seed: pass elif not seed <= _.MAX_SEED: raise AssertionError (seed, z) = divmod(seed, 0x7672L) (seed, y) = divmod(seed, 0x7662L) (x, y, z) = (int(seed + 1), int(y + 1), int(z + 1)) if __debug__: if x < x: pass elif not x < 30269: raise AssertionError if __debug__: if y < y: pass elif not y < 30307: raise AssertionError if __debug__: if z < z: pass elif not z < 30323: raise AssertionError return (x, y, z) def constructRandom(s): s = re.sub('L$', '', str(s)) s = re.sub('[\\s\\#\\-\\_\\.\\,]', '', string.lower(s)) if not s: return (None, None) if 1 and len(s) in (12, 16): if re.search('[^0-9a-f]', s): raise ValueError, s ss = s gameid = None if len(s) == 16: gameid = string.atoi(s[:4], 16) ss = s[4:] x = string.atoi(ss[0:4], 16) y = string.atoi(ss[4:8], 16) z = string.atoi(ss[8:12], 16) if x < x: pass elif x < 30269: if y < y: pass elif y < 30307: if z < z: pass elif z < 30323: return (gameid, WHRandom((x, y, z))) gameid = None if re.search('[^0-9]', s): raise ValueError, s seed = string.atol(s) if seed <= seed: pass elif seed <= 32000: return (gameid, LCRandom31(seed)) return (gameid, LCRandom64(seed)) SUITS = ('Club', 'Spade', 'Heart', 'Diamond') COLORS = ('black', 'red') RANKS = ('Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Jack', 'Queen', 'King') ACE = 0 JACK = 10 QUEEN = 11 KING = 12 ANY_SUIT = -1 ANY_COLOR = -1 ANY_RANK = -1 NO_SUIT = 999999 NO_COLOR = 999999 NO_RANK = 999999 NO_REDEAL = 0 UNLIMITED_REDEALS = -1 VARIABLE_REDEALS = -2 CARDSET = 'cardset' IMAGE_EXTENSIONS = ('.gif', '.ppm') if 1 and os.name == 'nt': IMAGE_EXTENSIONS = ('.png', '.gif', '.ppm', '.jpg') try: bundle except: bundle = 0 def get_version_tuple(version_string): v = re.split('[^\\d\\.]', version_string) if not v or not v[0]: return (0,) v = string.split(v[0], '.') v = filter((lambda x: x != ''), v) if not v or not v[0]: return (0,) return tuple(map(int, v)) class Timer: def __init__(_, msg = ''): _.msg = msg _.clock = time.time if os.name == 'nt': _.clock = time.clock _.start = _.clock() def reset(_): _.start = _.clock() def get(_): return _.clock() - _.start def __repr__(_): return '%-20s %6.3f seconds' % (_.msg, _.clock() - _.start) class DataLoader: def __init__(_, argv0, filenames, path = []): _.dir = None if type(filenames) is types.StringType: filenames = (filenames,) if not __debug__ and type(filenames) in (types.TupleType, types.ListType): raise AssertionError path = path[:] (head, tail) = os.path.split(argv0) if not head: head = os.curdir path.append(head) path.append(os.path.join(head, 'data')) if os.name == 'posix': pass if os.name == 'nt': pass if os.name == 'mac': pass _.path = [] for p in path: try: np = os.path.normpath(p) if np and not (np in _.path) and os.path.isdir(np): _.path.append(np) except EnvError: None if 1 and os.name == 'posix' else (VERSION, '') if not p else path None if 1 and os.name == 'posix' else (VERSION, '') if not p else path except: None if 1 and os.name == 'posix' else (VERSION, '') if not p else path for p in _.path: n = 0 for filename in filenames: try: f = os.path.join(p, filename) if os.path.isfile(f): n = n + 1 except EnvError: 0 0 filenames except: 0 else: raise os.error, str(argv0) + ': DataLoader could not find ' + str(filenames) def __findFile(_, func, filename, subdirs = None, do_raise = 1): if subdirs is None: subdirs = ('',) elif type(subdirs) is types.StringType: subdirs = (subdirs,) for dir in subdirs: f = os.path.join(_.dir, dir, filename) f = os.path.normpath(f) if do_raise: raise os.error, 'DataLoader could not find ' + filename + ' in ' + _.dir + ' ' + str(subdirs) return None def findFile(_, filename, subdirs = None): return _._DataLoader__findFile(os.path.isfile, filename, subdirs) def findImage(_, filename, subdirs = None): for ext in IMAGE_EXTENSIONS: f = _._DataLoader__findFile(os.path.isfile, filename + ext, subdirs, 0) raise os.error, 'DataLoader could not find image ' + filename + ' in ' + _.dir + ' ' + str(subdirs) def findIcon(_, filename = None, subdirs = None): if not filename: filename = string.lower(PACKAGE) (root, ext) = os.path.splitext(filename) if not ext: filename = filename + '.xbm' return _.findFile(filename, subdirs) def findDir(_, filename, subdirs = None): return _._DataLoader__findFile(os.path.isdir, filename, subdirs) cyclops = None class Resource(Struct): def __init__(_, **kw): kw = KwStruct(kw, name = '', filename = '', basename = '', absname = '', index = -1, error = 0) apply(Struct.__init__, (_,), kw.getKw()) def getSortKey(_): return string.lower(latin1_to_ascii(_.name)) class ResourceManager: def __init__(_): _._selected_key = -1 _._objects = [] _._objects_by_name = None _._objects_cache_name = { } _._objects_cache_filename = { } _._objects_cache_basename = { } _._objects_cache_absname = { } def getSelected(_): return _._selected_key def setSelected(_, index): if __debug__: if index <= index: pass elif not index < len(_._objects): raise AssertionError _._selected_key = index def len(_): return len(_._objects) def register(_, obj): if not __debug__ and obj.index == -1: raise AssertionError if __debug__: if not obj.name and not _._objects_cache_name.has_key(obj.name): raise AssertionError _._objects_cache_name[obj.name] = obj if obj.filename: obj.absname = os.path.abspath(obj.filename) obj.basename = os.path.basename(obj.filename) _._objects_cache_filename[obj.filename] = obj _._objects_cache_basename[obj.basename] = obj _._objects_cache_absname[obj.absname] = obj obj.index = len(_._objects) _._objects.append(obj) _._objects_by_name = None def get(_, index): if index <= index: pass elif index < len(_._objects): return _._objects[index] return None def getByName(_, key): return _._objects_cache_name.get(key) def getByBasename(_, key): return _._objects_cache_basename.get(key) def getAll(_): return tuple(_._objects) def getAllSortedByName(_): if _._objects_by_name is None: l = map((lambda obj: (obj.getSortKey(), obj)), _._objects) l.sort() _._objects_by_name = tuple(map((lambda item: item[1]), l)) return _._objects_by_name def _addDir(_, result, dir): try: if dir: dir = os.path.normpath(dir) if os.name == 'nt': dir = os.path.normcase(dir) if dir and os.path.isdir(dir) and not (dir in result): result.append(dir) except EnvError: ex = None def _addRegistryKey(_, result, hkey, subkey): KEY_READ = 131097 k = None try: k = win32api.RegOpenKeyEx(hkey, subkey, 0, KEY_READ) (nsubkeys, nvalues, t) = win32api.RegQueryInfoKey(k) for i in range(nvalues): try: (key, value, vtype) = win32api.RegEnumValue(k, i) except: 0 range(nvalues) break finally: if k is not None: try: win32api.RegCloseKey(k) except: pass def getSearchDirs(_, app, search, env = None): if type(search) is types.StringType: search = (search,) result = [] for dir in (app.dataloader.dir, app.dn.maint, app.dn.config): dir = os.path.normpath(dir) if not dir or not os.path.isdir(dir): continue for s in search: try: if s[-2:] == '-*': d = os.path.normpath(os.path.join(dir, s[:-2])) _._addDir(result, d) globdirs = glob.glob(d + '-*') globdirs.sort() for d in globdirs: _._addDir(result, d) else: _._addDir(result, os.path.join(dir, s)) except EnvError: 0 ex = 0 search except: 0 if app.debug >= 2: print 'getSearchDirs', env, search, '->', result return result def getRegistryDirs(_, app, categories): if not win32api: return [] HKEY_CURRENT_USER = -2147483647 HKEY_LOCAL_MACHINE = -2147483646 vendors = ('Markus Oberhumer', '') versions = (VERSION, '') if type(categories) is types.StringType: categories = (categories,) result = [] for version in versions: for vendor in vendors: for category in categories: t = ('Software', vendor, PACKAGE, version, category) t = filter(None, t) subkey = string.join(t, '\\') for hkey in (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE): try: _._addRegistryKey(result, hkey, subkey) except: 0 (HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE) 0 return result class CSI: SIZE_TINY = 1 SIZE_SMALL = 2 SIZE_MEDIUM = 3 SIZE_LARGE = 4 SIZE_XLARGE = 5 TYPE_FRENCH = 1 TYPE_HANAFUDA = 2 TYPE_TAROCK = 3 TYPE_MAHJONGG = 4 TYPE_HEXADECK = 5 TYPE_MUGHAL_GANJIFA = 6 TYPE_NAVAGRAHA_GANJIFA = 7 TYPE_DASHAVATARA_GANJIFA = 8 TYPE_TRUMP_ONLY = 9 TYPE = { 1: 'French type (52 cards)', 2: 'Hanafuda type (48 cards)', 3: 'Tarock type (78 cards)', 5: 'Hex A Deck type (68 cards)', 6: 'Mughal Ganjifa type (96 cards)', 7: 'Navagraha Ganjifa type (108 cards)', 8: 'Dashavatara Ganjifa type (120 cards)', 9: 'Trumps only type (variable cards)' } STYLE = { 1: 'Adult', 2: 'Animals', 3: 'Anime', 4: 'Art', 5: 'Cartoons', 6: 'Children', 7: 'Classic look', 8: 'Collectors', 9: 'Computers', 10: 'Engines', 11: 'Fantasy', 30: 'Ganjifa', 12: 'Hanafuda', 29: 'Hex A Deck', 13: 'Holiday', 14: 'Movies', 31: 'Matrix', 15: 'Music', 16: 'Nature', 17: 'Operating Systems', 19: 'People', 20: 'Places', 21: 'Plain', 22: 'Products', 18: 'Round cardsets', 23: 'Science Fiction', 24: 'Sports', 27: 'Tarock', 25: 'Vehicels', 26: 'Video Games' } NATIONALITY = { 1021: 'Australia', 1001: 'Austria', 1019: 'Belgium', 1010: 'Canada', 1011: 'China', 1012: 'Czech Republic', 1013: 'Denmark', 1003: 'England', 1004: 'France', 1006: 'Germany', 1014: 'Great Britain', 1015: 'Hungary', 1020: 'India', 1005: 'Italy', 1016: 'Japan', 1002: 'Netherlands', 1007: 'Russia', 1008: 'Spain', 1017: 'Sweden', 1009: 'Switzerland', 1018: 'USA' } DATE = { 10: '1000 - 1099', 11: '1100 - 1199', 12: '1200 - 1299', 13: '1300 - 1399', 14: '1400 - 1499', 15: '1500 - 1599', 16: '1600 - 1699', 17: '1700 - 1799', 18: '1800 - 1899', 19: '1900 - 1999', 20: '2000 - 2099', 21: '2100 - 2199', 22: '2200 - 2299' } class CardsetConfig(Struct): def __init__(_): Struct.__init__(_, version = 1, ext = '.gif', type = CSI.TYPE_FRENCH, ncards = -1, styles = [], year = 0, ident = '', name = '', CARDW = 0, CARDH = 0, CARDD = 0, CARD_UP_YOFFSET = 0, CARD_DOWN_YOFFSET = 0, SHADOW_XOFFSET = 0, SHADOW_YOFFSET = 0, backindex = 0, backnames = (), CARD_DX = 0, CARD_DY = 0) class Cardset(Resource): def __init__(_, **kw): config = CardsetConfig() kw = apply(KwStruct, (config.__dict__,), kw) si = Struct(type = 0, size = 0, styles = [], nationalities = [], dates = []) kw = KwStruct(kw, ranks = (), suits = (), trumps = (), nbottoms = 7, nletters = 4, nshadows = 1 + 13, si = si, backname = None, dir = '') apply(Resource.__init__, (_,), kw.getKw()) def getFaceCardNames(_): names = [] for suit in _.suits: for rank in _.ranks: names.append('%02d%s' % (rank + 1, suit)) for trump in _.trumps: names.append('%02d%s' % (trump + 1, 'z')) if not __debug__ and len(names) == _.ncards: raise AssertionError 0 return names def getPreviewCardNames(_): names = _.getFaceCardNames() pnames = [] (ranks, suits) = (_.ranks, _.suits) (lr, ls) = (len(ranks), len(suits)) if lr == 0 or ls == 0: return (names[:16], 4) if lr >= 4: ls = min(ls, 4) (low_ranks, high_ranks) = (1, 3) for rank in range(0, low_ranks) + range(lr - high_ranks, lr): for suit in range(ls): index = suit * len(_.ranks) + rank pnames.append(names[index % len(names)]) return (pnames, ls) def updateCardback(_, backname = None, backindex = None): if type(backname) is types.StringType: if backname in _.backnames: backindex = _.backnames.index(backname) if type(backindex) is types.IntType: _.backindex = backindex % len(_.backnames) _.backname = _.backnames[_.backindex] class CardsetManager(ResourceManager): def __init__(_): ResourceManager.__init__(_) _.registered_types = { } _.registered_sizes = { } _.registered_styles = { } _.registered_nationalities = { } _.registered_dates = { } def _check(_, cs): s = cs.type if not CSI.TYPE.has_key(s): return 0 cs.si.type = s if s == CSI.TYPE_FRENCH: cs.ranks = range(13) cs.suits = 'cshd' elif s == CSI.TYPE_HANAFUDA: cs.ranks = range(12) cs.suits = 'cshd' elif s == CSI.TYPE_TAROCK: cs.nbottoms = 8 cs.ranks = range(14) cs.suits = 'cshd' cs.trumps = range(22) elif s == CSI.TYPE_HEXADECK: cs.nbottoms = 8 cs.ranks = range(16) cs.suits = 'cshd' cs.trumps = range(4) elif s == CSI.TYPE_MUGHAL_GANJIFA: cs.nbottoms = 11 cs.ranks = range(12) cs.suits = 'abcdefgh' elif s == CSI.TYPE_NAVAGRAHA_GANJIFA: return 0 cs.nbottoms = 12 cs.ranks = range(12) cs.suits = 'abcdefghi' elif s == CSI.TYPE_DASHAVATARA_GANJIFA: cs.nbottoms = 13 cs.ranks = range(12) cs.suits = 'abcdefghij' elif s == CSI.TYPE_TRUMP_ONLY: return 0 cs.nbottoms = 7 cs.ranks = () cs.suits = '' cs.trumps = range(cs.ncards) else: return 0 return 1 def register(_, cs): if not _._check(cs): return None cs.ncards = len(cs.ranks) * len(cs.suits) + len(cs.trumps) cs.name = cs.name[:25] if not None if cs.si.size <= cs.si.size else cs.si.size <= 5: (CW, CH) = (cs.CARDW, cs.CARDH) if CW <= 55 and CH <= 72: cs.si.size = CSI.SIZE_TINY elif CW <= 60 and CH <= 85: cs.si.size = CSI.SIZE_SMALL elif CW <= 75 and CH <= 105: cs.si.size = CSI.SIZE_MEDIUM elif CW <= 90 and CH <= 125: cs.si.size = CSI.SIZE_LARGE else: cs.si.size = CSI.SIZE_XLARGE keys = cs.styles[:] cs.si.styles = tuple(filter((lambda s: CSI.STYLE.has_key(s)), keys)) for s in cs.si.styles: _.registered_styles[s] = _.registered_styles.get(s, 0) + 1 cs.si.nationalities = tuple(filter((lambda s: CSI.NATIONALITY.has_key(s)), keys)) for s in cs.si.nationalities: _.registered_nationalities[s] = _.registered_nationalities.get(s, 0) + 1 keys = (cs.year / 100,) cs.si.dates = tuple(filter((lambda s: CSI.DATE.has_key(s)), keys)) for s in cs.si.dates: _.registered_dates[s] = _.registered_dates.get(s, 0) + 1 s = cs.si.type _.registered_types[s] = _.registered_types.get(s, 0) + 1 s = cs.si.size _.registered_sizes[s] = _.registered_sizes.get(s, 0) + 1 cs.updateCardback() ResourceManager.register(_, cs) class Tile(Resource): def __init__(_, **kw): kw = KwStruct(kw, color = None, text_color = '#000000') apply(Resource.__init__, (_,), kw.getKw()) class TileManager(ResourceManager): pass class Sample(Resource): def __init__(_, **kw): kw = KwStruct(kw, volume = -1) apply(Resource.__init__, (_,), kw.getKw()) class SampleManager(ResourceManager): pass class Music(Sample): pass class MusicManager(SampleManager): pass class GI: GC_FRENCH = CSI.TYPE_FRENCH GC_HANAFUDA = CSI.TYPE_HANAFUDA GC_TAROCK = CSI.TYPE_TAROCK GC_MAHJONGG = CSI.TYPE_MAHJONGG GC_HEXADECK = CSI.TYPE_HEXADECK GC_MUGHAL_GANJIFA = CSI.TYPE_MUGHAL_GANJIFA GC_NAVAGRAHA_GANJIFA = CSI.TYPE_NAVAGRAHA_GANJIFA GC_DASHAVATARA_GANJIFA = CSI.TYPE_DASHAVATARA_GANJIFA GC_TRUMP_ONLY = CSI.TYPE_TRUMP_ONLY GT_1DECK_TYPE = 0 GT_2DECK_TYPE = 1 GT_3DECK_TYPE = 2 GT_4DECK_TYPE = 3 GT_BAKERS_DOZEN = 4 GT_BELEAGUERED_CASTLE = 5 GT_CANFIELD = 6 GT_DASHAVATARA_GANJIFA = 7 GT_FAN_TYPE = 8 GT_FORTY_THIEVES = 9 GT_FREECELL = 10 GT_GOLF = 11 GT_GYPSY = 12 GT_HANAFUDA = 13 GT_HEXADECK = 14 GT_KLONDIKE = 15 GT_MAHJONGG = 16 GT_MATRIX = 17 GT_MEMORY = 18 GT_MONTANA = 19 GT_MUGHAL_GANJIFA = 20 GT_NAPOLEON = 21 GT_NAVAGRAHA_GANJIFA = 22 GT_NUMERICA = 23 GT_PAIRING_TYPE = 24 GT_POKER_TYPE = 25 GT_PUZZLE_TYPE = 26 GT_RAGLAN = 27 GT_ROW_TYPE = 28 GT_SIMPLE_TYPE = 29 GT_SPIDER = 30 GT_TAROCK = 31 GT_TERRACE = 32 GT_YUKON = 33 GT_BETA = 1 << 12 GT_CHILDREN = 1 << 13 GT_CONTRIB = 1 << 14 GT_HIDDEN = 1 << 15 GT_OPEN = 1 << 16 GT_ORIGINAL = 1 << 17 GT_POPULAR = 1 << 18 GT_RELAXED = 1 << 19 GT_SCORE = 1 << 20 GT_SEPARATE_DECKS = 1 << 21 GT_XORIGINAL = 1 << 22 SELECT_GAME_BY_TYPE = (("Baker's Dozen type", (lambda gi, gt = GT_BAKERS_DOZEN: gi.si.game_type == gt)), ('Beleaguered Castle type', (lambda gi, gt = GT_BELEAGUERED_CASTLE: gi.si.game_type == gt)), ('Canfield type', (lambda gi, gt = GT_CANFIELD: gi.si.game_type == gt)), ('Fan type', (lambda gi, gt = GT_FAN_TYPE: gi.si.game_type == gt)), ('Forty Thieves type', (lambda gi, gt = GT_FORTY_THIEVES: gi.si.game_type == gt)), ('FreeCell type', (lambda gi, gt = GT_FREECELL: gi.si.game_type == gt)), ('Golf type', (lambda gi, gt = GT_GOLF: gi.si.game_type == gt)), ('Gypsy type', (lambda gi, gt = GT_GYPSY: gi.si.game_type == gt)), ('Klondike type', (lambda gi, gt = GT_KLONDIKE: gi.si.game_type == gt)), ('Montana type', (lambda gi, gt = GT_MONTANA: gi.si.game_type == gt)), ('Napoleon type', (lambda gi, gt = GT_NAPOLEON: gi.si.game_type == gt)), ('Numerica type', (lambda gi, gt = GT_NUMERICA: gi.si.game_type == gt)), ('Pairing type', (lambda gi, gt = GT_PAIRING_TYPE: gi.si.game_type == gt)), ('Raglan type', (lambda gi, gt = GT_RAGLAN: gi.si.game_type == gt)), ('Simple games', (lambda gi, gt = GT_SIMPLE_TYPE: gi.si.game_type == gt)), ('Spider type', (lambda gi, gt = GT_SPIDER: gi.si.game_type == gt)), ('Terrace type', (lambda gi, gt = GT_TERRACE: gi.si.game_type == gt)), ('Yukon type', (lambda gi, gt = GT_YUKON: gi.si.game_type == gt)), ('One-Deck games', (lambda gi, gt = GT_1DECK_TYPE: gi.si.game_type == gt)), ('Two-Deck games', (lambda gi, gt = GT_2DECK_TYPE: gi.si.game_type == gt)), ('Three-Deck games', (lambda gi, gt = GT_3DECK_TYPE: gi.si.game_type == gt)), ('Four-Deck games', (lambda gi, gt = GT_4DECK_TYPE: gi.si.game_type == gt))) SELECT_SPECIAL_GAME_BY_TYPE = (('Dashavatara Ganjifa type', (lambda gi, gt = GT_DASHAVATARA_GANJIFA: gi.si.game_type == gt)), ('Hanafuda type', (lambda gi, gt = GT_HANAFUDA: gi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gt = GT_HEXADECK: gi.si.game_type == gt)), ('Matrix type', (lambda gi, gt = GT_MATRIX: gi.si.game_type == gt)), ('Mughal Ganjifa type', (lambda gi, gt = GT_MUGHAL_GANJIFA: gi.si.game_type == gt)), ('Navagraha Ganjifa type', (lambda gi, gt = GT_NAVAGRAHA_GANJIFA: gi.si.game_type == gt)), ('Memory type', (lambda gi, gt = GT_MEMORY: gi.si.game_type == gt)), ('Poker type', (lambda gi, gt = GT_POKER_TYPE: gi.si.game_type == gt)), ('Puzzle type', (lambda gi, gt = GT_PUZZLE_TYPE: gi.si.game_type == gt)), ('Tarock type', (lambda gi, gt = GT_TAROCK: gi.si.game_type == gt))) SELECT_ORIGINAL_GAME_BY_TYPE = (('French type', (lambda gi, gf = GT_ORIGINAL, gt = (GT_HANAFUDA, GT_HEXADECK, GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA, GT_TAROCK): if gi.si.game_flags & gf: passgi.si.game_type not in gt)), ('Ganjifa type', (lambda gi, gf = GT_ORIGINAL, gt = (GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA): if gi.si.game_flags & gf: passgi.si.game_type in gt)), ('Hanafuda type', (lambda gi, gf = GT_ORIGINAL, gt = GT_HANAFUDA: if gi.si.game_flags & gf: passgi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gf = GT_ORIGINAL, gt = GT_HEXADECK: if gi.si.game_flags & gf: passgi.si.game_type == gt)), ('Tarock type', (lambda gi, gf = GT_ORIGINAL, gt = GT_TAROCK: if gi.si.game_flags & gf: passgi.si.game_type == gt))) SELECT_CONTRIB_GAME_BY_TYPE = (('French type', (lambda gi, gf = GT_CONTRIB, gt = (GT_HANAFUDA, GT_HEXADECK, GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA, GT_TAROCK): if gi.si.game_flags & gf: passgi.si.game_type not in gt)), ('Ganjifa type', (lambda gi, gf = GT_CONTRIB, gt = (GT_MUGHAL_GANJIFA, GT_NAVAGRAHA_GANJIFA, GT_DASHAVATARA_GANJIFA): if gi.si.game_flags & gf: passgi.si.game_type in gt)), ('Hanafuda type', (lambda gi, gf = GT_CONTRIB, gt = GT_HANAFUDA: if gi.si.game_flags & gf: passgi.si.game_type == gt)), ('Hex A Deck type', (lambda gi, gf = GT_CONTRIB, gt = GT_HEXADECK: if gi.si.game_flags & gf: passgi.si.game_type == gt)), ('Tarock type', (lambda gi, gf = GT_CONTRIB, gt = GT_TAROCK: if gi.si.game_flags & gf: passgi.si.game_type == gt))) PROTECTED_GAMES = { 22: 106, 32: 901, 52: 903, 72: 115, 75: 126, 82: 901, 262: 105, 902: 88, 904: 68 } GAMES_BY_COMPATIBILITY = (('Atari ST Patience', (1, 3, 4, 7, 12, 14, 15, 16, 17, 39)), ('Gnome AisleRiot', (2, 8, 11, 19, 27, 29, 33, 34, 35, 40, 41, 42, 43, 58, 59, 92, 93, 94, 95, 96, 100, 105, 111, 112, 113, 130, 200, 201)), ('KDE Patience', (2, 7, 8, 18, 256, 903)), ('xpat2', (1, 2, 8, 9, 11, 31, 54, 63, 89, 105, 901, 256, 345, 903))) GAMES_BY_PYSOL_VERSION = (('1.00', (1, 2, 3, 4)), ('1.01', (5, 6)), ('1.02', (7, 8, 9)), ('1.03', (10, 11, 12, 13)), ('1.10', (14,)), ('1.11', (15, 16, 17)), ('2.00', (256, 257)), ('2.01', (258, 259, 260, 261)), ('2.02', (105,)), ('2.90', (18, 19, 20, 21, 106, 23, 24, 25, 26, 27, 28, 29, 30, 31, 901, 33, 34, 35, 36)), ('2.99', (37,)), ('3.00', (38, 39, 40, 41, 42, 43, 45, 46, 47, 48, 49, 50, 51, 903, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 115, 73, 74, 126, 76, 77, 78, 79, 80, 81, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 107, 108)), ('3.10', (109, 110, 111, 112, 113, 114, 116, 117, 118, 119, 120, -121, -122, 123, 124, 125, 127)), ('3.20', (128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 345, 346, 347, 348, 349, 350, 351, 352)), ('3.21', (143, 144)), ('3.30', (145, 146, 147, 148, 149, 150, 151)), ('3.40', (152, 153, 154)), ('4.00', (157, 158, 159, 160, 161, 162, 163, 164)), ('4.20', (165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178)), ('4.30', (179, 180, 181, 182, 183, 184)), ('4.41', (185, 186, -187, -188, -189, -190, -191, -192, 193, -194, 195, 196, -197, -198, 199)), ('4.60', (200, 201, 202, 203, 204, 205))) _CHILDREN_GAMES = [ 16, 33, 55, 90, 91, 96, 97, 176, 903] _OPEN_GAMES = [ 5, 6, 8, 9, 26, 31, 45, 46, 50, 53, 63, 64, 77, 85, 86, 96, 116, 117, 118, 258] _POPULAR_GAMES = [ 1, 2, 7, 8, 11, 12, 13, 14, 19, 31, 36, 38, 105, 158, 345, 903] def assertGI(_, manager = None): for key, games in _.GAMES_BY_COMPATIBILITY: games = filter((lambda id: id >= 0), games) print '%-20s: %d games' % (key, len(games)) for id in games[:]: if not __debug__ and type(id) is types.IntType: raise AssertionError 0 all_games = [] for key, games in _.GAMES_BY_PYSOL_VERSION: games = filter((lambda id: id >= 0), games) print '%s: %3d games, %3d total' % (key, len(games), len(all_games) + len(games)) for id in games[:]: if not __debug__ and type(id) is types.IntType: raise AssertionError 0 if _.PROTECTED_GAMES.get(id): print id if not __debug__ and 0: raise AssertionError, id _.GAMES_BY_PYSOL_VERSION if id in all_games: print id if not __debug__ and 0: raise AssertionError, id if manager and not manager.get(id): print id if not __debug__ and 0: raise AssertionError, id all_games.extend(list(games)) print 'total:', len(all_games), 'games' if manager: g = manager.getGamesIdSortedById() g = filter((lambda id, m = manager: m.get(id).plugin == 0), g) g = list(g)[:] for id in all_games: g.remove(id) if g: print 'WARNING: games without version:', g class GameInfoException(Exception): pass class GameInfo(Struct): def __init__(_, id, gameclass, name, game_type, decks, redeals, si = { }, category = 0, short_name = None, altnames = None, suits = range(4), ranks = range(13), trumps = (), rules_filename = None): ncards = decks * (len(suits) * len(ranks) + len(trumps)) game_flags = game_type & ~1023 game_type = game_type & 1023 if os.name == 'mac': name = latin1_to_ascii(name) if not short_name: short_name = name if type(altnames) is types.StringType: altnames = (altnames,) if not altnames: altnames = () if not None if id <= id else id <= 999999: raise GameInfoException, name + ': invalid game ID ' + str(id) if not None if decks <= decks else decks <= 4: raise GameInfoException, name + ': invalid number of decks ' + str(id) if not name or not None if len(short_name) <= len(short_name) else len(short_name) <= 25: raise GameInfoException, name + ': invalid game name' if GI.PROTECTED_GAMES.get(id): raise GameInfoException, name + ': protected game ID ' + str(id) for f, l in ((GI.GT_CHILDREN, GI._CHILDREN_GAMES), (GI.GT_OPEN, GI._OPEN_GAMES), (GI.GT_POPULAR, GI._POPULAR_GAMES)): if game_flags & f and id not in l: l.append(id) elif not (game_flags & f) and id in l: game_flags = game_flags | f if not None if category <= category else category <= 9: if game_type == GI.GT_HANAFUDA: category = GI.GC_HANAFUDA elif game_type == GI.GT_TAROCK: category = GI.GC_TAROCK elif game_type == GI.GT_MAHJONGG: category = GI.GC_MAHJONGG elif game_type == GI.GT_HEXADECK: category = GI.GC_HEXADECK elif game_type == GI.GT_MUGHAL_GANJIFA: category = GI.GC_MUGHAL_GANJIFA elif game_type == GI.GT_NAVAGRAHA_GANJIFA: category = GI.GC_NAVAGRAHA_GANJIFA elif game_type == GI.GT_DASHAVATARA_GANJIFA: category = GI.GC_DASHAVATARA_GANJIFA else: category = GI.GC_FRENCH gi_si = Struct(game_type = game_type, game_flags = game_flags, decks = decks, redeals = redeals, ncards = ncards) gi_si.update(si) Struct.__init__(_, id = id, gameclass = gameclass, name = name, short_name = short_name, altnames = tuple(altnames), decks = decks, redeals = redeals, ncards = ncards, category = category, suits = tuple(suits), ranks = tuple(ranks), trumps = tuple(trumps), si = gi_si, rules_filename = rules_filename, plugin = 0) class GameManager: def __init__(_): _._GameManager__selected_key = -1 _._GameManager__games = { } _._GameManager__gamenames = { } _._GameManager__games_by_id = None _._GameManager__games_by_name = None _._GameManager__games_by_short_name = None _._GameManager__games_by_altname = None _._GameManager__all_games = { } _._GameManager__all_gamenames = { } _.loading_plugin = 0 _.registered_game_types = { } def getSelected(_): return _._GameManager__selected_key def setSelected(_, gameid): if not __debug__ and _._GameManager__all_games.has_key(gameid): raise AssertionError _._GameManager__selected_key = gameid def get(_, key): return _._GameManager__all_games.get(key) def register(_, gi): if not isinstance(gi, GameInfo): raise GameInfoException, 'wrong GameInfo class' gi.plugin = _.loading_plugin if _._GameManager__all_games.has_key(gi.id): raise GameInfoException, 'duplicate game ID ' + str(gi.id) if _._GameManager__all_gamenames.has_key(gi.name): raise GameInfoException, 'duplicate game name ' + str(gi.id) + ': ' + game.name if gi.si.game_flags & GI.GT_XORIGINAL: return None if 1: if gi.id <= gi.id: pass elif gi.id <= 236: return None _._GameManager__all_games[gi.id] = gi _._GameManager__all_gamenames[gi.name] = gi for n in gi.altnames: _._GameManager__all_gamenames[n] = gi def getGamesIdSortedById(_): if _._GameManager__games_by_id is None: l = _._GameManager__games.keys() l.sort() _._GameManager__games_by_id = tuple(l) return _._GameManager__games_by_id def getGamesIdSortedByName(_): return _._GameManager__games_by_name def getGamesIdSortedByShortName(_): if _._GameManager__games_by_name is None: _.getGamesIdSortedByName() return _._GameManager__games_by_short_name def getGamesTuplesSortedByAlternateName(_): if _._GameManager__games_by_name is None: _.getGamesIdSortedByName() return _._GameManager__games_by_altname GAME_DB = GameManager() def registerGame(gameinfo): GAME_DB.register(gameinfo) return gameinfo def loadGame(modname, filename, plugin = 1): GAME_DB.loading_plugin = plugin if os.name == 'nt': return None execfile(filename, globals(), globals()) class AbstractAudioClient: def __init__(_): _.server = None _.audiodev = None _.connected = 0 _.app = None _.file_cache = { } _.sample_priority = -1 _.sample_loop = 0 _.music_priority = -1 _.music_loop = 0 def __del__(_): _.destroy() def startServer(_): pass def connectServer(_, app): if not __debug__ and app: raise AssertionError _.app = app if _.server is not None: try: if _._connectServer(): _.connected = 1 except: if traceback: traceback.print_exc() _.destroy() def destroy(_): if _.audiodev is not None: try: _._destroy() except: pass _.server = None _.audiodev = None _.connected = 0 _.app = None def stopAll(_): _.stopSamples() _.stopMusic() def playSample(_, name, priority = 0, loop = 0, volume = -1): if _.audiodev is None and not (_.app) or not (_.app.opt.sound): return 0 if priority <= _.sample_priority and _.sample_loop: return 0 obj = _.app.sample_manager.getByName(name) if not obj or not (obj.absname): return 0 try: if _._playSample(obj.absname, priority, loop, volume): _.sample_priority = priority _.sample_loop = loop return 1 except: if traceback: traceback.print_exc() return 0 def stopSamples(_): if _.audiodev is None: return None try: _._stopSamples() except: if traceback: traceback.print_exc() _.sample_priority = -1 _.sample_loop = 0 def stopSamplesLoop(_): if _.audiodev is None: return None try: _._stopSamplesLoop() except: if traceback: traceback.print_exc() _.sample_priority = -1 _.sample_loop = 0 def playMusic(_, basename, priority = 0, loop = 0, volume = -1): if _.audiodev is None and not (_.app) or not (_.app.opt.sound): return 0 if priority <= _.music_priority and _.music_loop: return 0 obj = _.app.music_manager.getByBasename(basename) if not obj or not (obj.absname): return 0 try: if _._playMusic(obj.absname, priority, loop, volume): _.music_priority = priority _.music_loop = loop return 1 except: if traceback: traceback.print_exc() return 0 def stopMusic(_): if _.audiodev is None: return None try: _._stopMusic() except: if traceback: traceback.print_exc() _.music_priority = -1 _.music_loop = 0 def _connectServer(_): return 0 def _destroy(_): pass def _playSample(_, name, priority, loop): return 0 def _stopSamples(_): pass def _stopSamplesLoop(_): _._stopSamples() def _playMusic(_, name, priority, loop, volume): return 0 def _stopMusic(_): pass def getMusicInfo(_): return -1 def playContinuousMusic(_, music_list): pass def playNextMusic(_): pass def updateSettings(_): pass class PysolSoundServerModuleClient(AbstractAudioClient): def startServer(_): try: _.audiodev = pysolsoundserver _.audiodev.init() _.server = 1 except: if traceback: traceback.print_exc() _.server = None _.audiodev = None def cmd(_, cmd): _.audiodev.cmd(cmd) def _connectServer(_): _.cmd('protocol 4') if 0 and _.app.debug: _.cmd('debug 1') return 1 def _destroy(_): _.audiodev.exit() def _playSample(_, filename, priority, loop, volume): _.cmd("playwav '%s' %d %d %d %d" % (filename, -1, priority, loop, volume)) return 1 def _stopSamples(_): _.cmd('stopwav') def _stopSamplesLoop(_): _.cmd('stopwavloop') def _playMusic(_, filename, priority, loop, volume): _.cmd("playmus '%s' %d %d %d %d" % (filename, -1, priority, loop, volume)) return 1 def _stopMusic(_): _.cmd('stopmus') def getMusicInfo(_): if _.audiodev: return _.audiodev.getMusicInfo() return -1 def playContinuousMusic(_, music_list): if _.audiodev is None or not (_.app): return None try: for music in music_list: pass _.cmd('startqueue') except: if traceback: traceback.print_exc() def playNextMusic(_): _.cmd('nextmus') def updateSettings(_): if _.audiodev is None or not (_.app): return None (s, m) = (0, 0) if _.app.opt.sound: s = _.app.opt.sound_sample_volume m = _.app.opt.sound_music_volume try: _.cmd('setwavvol %d' % s) _.cmd('setmusvol %d' % m) except: if traceback: traceback.print_exc() class Win32AudioClient(AbstractAudioClient): def startServer(_): try: import winsound _.audiodev = winsound del winsound _.server = 0 except: _.server = None _.audiodev = None def _playSample(_, filename, priority, loop, volume): a = _.audiodev flags = a.SND_FILENAME | a.SND_NODEFAULT | a.SND_NOWAIT | a.SND_ASYNC if loop: flags = flags | a.SND_LOOP if priority <= _.sample_priority: flags = flags | a.SND_NOSTOP try: a.PlaySound(filename, flags) return 1 except: pass return 0 def _stopSamples(_): a = _.audiodev flags = a.SND_NODEFAULT | a.SND_PURGE a.PlaySound(None, flags) def Misc__destroy(_): pass def Canvas__tag_bind(_, tagOrId, sequence = None, func = None, add = None): return _._bind((_._w, 'bind', tagOrId), sequence, func, add) def Wm__wm_state(_, newstate = None): return _.tk.call('wm', 'state', _._w, newstate) def Text__xview_moveto(_, fraction): return _.tk.call(_._w, 'xview', 'moveto', fraction) def Text__xview_scroll(_, number, what): return _.tk.call(_._w, 'xview', 'scroll', number, what) def Text__yview_moveto(_, fraction): return _.tk.call(_._w, 'yview', 'moveto', fraction) def Text__yview_scroll(_, number, what): return _.tk.call(_._w, 'yview', 'scroll', number, what) Tkinter.Misc.destroy = Misc__destroy Tkinter.Canvas.tag_bind = Canvas__tag_bind Tkinter.Wm.wm_state = Wm__wm_state Tkinter.Wm.state = Wm__wm_state Tkinter.Text.xview_moveto = Text__xview_moveto Tkinter.Text.xview_scroll = Text__xview_scroll Tkinter.Text.yview_moveto = Text__yview_moveto Tkinter.Text.yview_scroll = Text__yview_scroll def CanvasItem__bbox(_): return _.canvas.bbox(_.id) def Group__bbox(_): return _.canvas.bbox(_.id) def CanvasItem__bind(_, sequence = None, command = None, add = None): return _.canvas.tag_bind(_.id, sequence, command, add) def Group__bind(_, sequence = None, command = None, add = None): return _.canvas.tag_bind(_.id, sequence, command, add) def CanvasItem__unbind(_, sequence, funcid = None): return _.canvas.tag_unbind(_.id, sequence, funcid) def Group__unbind(_, sequence, funcid = None): return _.canvas.tag_unbind(_.id, sequence, funcid) def CanvasItem__tkraise(_, abovethis = None): return _.canvas.tag_raise(_.id, abovethis) def CanvasItem__lower(_, belowthis = None): return _.canvas.tag_lower(_.id, belowthis) def Group__tkraise(_, abovethis = None): return _.canvas.tag_raise(_.id, abovethis) def Group__lower(_, belowthis = None): return _.canvas.tag_lower(_.id, belowthis) Canvas.CanvasItem.bbox = CanvasItem__bbox Canvas.Group.bbox = Group__bbox Canvas.CanvasItem.bind = CanvasItem__bind Canvas.Group.bind = Group__bind Canvas.CanvasItem.unbind = CanvasItem__unbind Canvas.Group.unbind = Group__unbind Canvas.CanvasItem.tkraise = CanvasItem__tkraise Canvas.CanvasItem.lower = CanvasItem__lower Canvas.Group.tkraise = Group__tkraise Canvas.Group.lower = Group__lower def CallWrapper____call__(_, *args): if _.subst: args = apply(_.subst, args) return apply(_.func, args) Tkinter.CallWrapper.__call__ = CallWrapper____call__ tkname = 'tk' tkversion = (8, 0, 0, 0) try: m = string.split(str(Tkinter._tkinter.TK_VERSION), '.') if m: m = map(int, m) + 4 * [ 0] tkversion = tuple(m[:4]) del m except: pass TK_DASH_PATCH = 0 EVENT_HANDLED = 'break' EVENT_PROPAGATE = None CURSOR_DRAG = 'hand1' CURSOR_WATCH = 'watch' ANCHOR_CENTER = Tkinter.CENTER ANCHOR_N = Tkinter.N ANCHOR_NW = Tkinter.NW ANCHOR_NE = Tkinter.NE ANCHOR_S = Tkinter.S ANCHOR_SW = Tkinter.SW ANCHOR_SE = Tkinter.SE ANCHOR_W = Tkinter.W ANCHOR_E = Tkinter.E def wm_withdraw(window): window.wm_withdraw() def wm_deiconify(window): if os.name == 'nt': pass need_fix = tkversion < (8, 3, 0, 0) if need_fix: try: window.wm_iconify() window.update_idletasks() except Tkinter.TclError: pass window.wm_deiconify() def wm_map(window, maximized = 0): if window.wm_state() != 'iconic': if maximized and os.name == 'nt': window.wm_state('zoomed') else: wm_deiconify(window) def wm_set_icon(window, filename): if not filename: return None if os.name == 'posix': window.wm_iconbitmap('@' + filename) window.wm_iconmask('@' + filename) def wm_parse_geometry(geometry): m = re.search('^(\\d+)x(\\d+)\\+([\\-]?\\d+)\\+([\\-]?\\d+)$', geometry) if m: return tuple(map(int, m.groups())) raise Tkinter.TclError, 'invalid geometry ' + str(geometry) def setTransient(window, parent, relx = None, rely = None, expose = 1): window.wm_withdraw() window.wm_group(parent) if os.name == 'nt': pass need_fix = tkversion < (8, 3, 0, 0) if need_fix: window.wm_geometry('+%d+%d' % (-10000, -10000)) if expose and parent is not None: window.wm_iconify() if parent and parent.wm_state() != 'withdrawn': window.wm_transient(parent) window.update_idletasks() (x, y) = __getWidgetXY(window, parent, relx = relx, rely = rely) if need_fix: if expose: wm_deiconify(window) window.wm_geometry('+%d+%d' % (x, y)) else: window.wm_geometry('+%d+%d' % (x, y)) if expose: window.wm_deiconify() def makeToplevel(parent, title = None, class_ = None): if class_: window = Tkinter.Toplevel(parent, class_ = class_) else: window = Tkinter.Toplevel(parent) if title: window.wm_title(title) window.wm_iconname(title) return window makeHelpToplevel = makeToplevel def __getWidgetXY(widget, parent, relx = None, rely = None, w_width = None, w_height = None): if w_width is None: w_width = widget.winfo_reqwidth() if w_height is None: w_height = widget.winfo_reqheight() s_width = widget.winfo_screenwidth() s_height = widget.winfo_screenheight() m_x = m_y = 0 (m_width, m_height) = (s_width, s_height) if parent and parent.winfo_ismapped(): m_x = m_y = None if os.name == 'nt': try: g = parent.wm_geometry() (m_width, m_height, m_x, m_y) = wm_parse_geometry(g) if parent.wm_state() == 'zoomed': m_x = m_y = 0 except: pass if m_x is None: m_x = parent.winfo_x() m_y = parent.winfo_y() m_width = parent.winfo_width() m_height = parent.winfo_height() if relx is None: relx = 0.5 if rely is None: rely = 0.3 elif relx is None: relx = 0.5 if rely is None: rely = 0.5 m_x = max(m_x, 0) m_y = max(m_y, 0) elif relx is None: relx = 0.5 if rely is None: rely = 0.3 x = m_x + int((m_width - w_width) * relx) y = m_y + int((m_height - w_height) * rely) if x < 0: x = 0 elif x + w_width + 32 > s_width: x = max(0, (s_width - w_width) / 2) if y < 0: y = 0 elif y + w_height + 32 > s_height: y = max(0, (s_height - w_height) / 2) return (x, y) __mfx_bindings = { } def bind(widget, sequence, func, add = None): if not __debug__ and callable(func): raise AssertionError if sequence == 'WM_DELETE_WINDOW': funcid = widget._register(func) widget.tk.call('wm', 'protocol', widget._w, sequence, funcid) elif add is None: funcid = widget.bind(sequence, func) else: funcid = widget.bind(sequence, func, add) k = id(widget) if __mfx_bindings.has_key(k): __mfx_bindings[k].append((sequence, funcid)) else: __mfx_bindings[k] = [ (sequence, funcid)] def unbind_destroy(widget): if widget is None: return None k = id(widget) def after(widget, ms, func, *args): timer = apply(widget.after, (ms, func) + args) command = widget._tclCommands[-1] return (timer, command, widget) def after_idle(widget, func, *args): return apply(after, (widget, 'idle', func) + args) def after_cancel(t): if t is not None: t[2].after_cancel(t[0]) try: t[2].deletecommand(t[1]) except Tkinter.TclError: pass getFont_cache = { } def getFont(name, cardw = 0): key = (name, cardw) font = getFont_cache.get(key) if font: return font font = ('Helvetica', '14') if os.name == 'nt': font = ('MS Sans Serif', '10') elif os.name == 'mac': font = ('Chicago', '12') if name in ('canvas', 'canvas_small', 'small', 'tree_small'): font = ('Helvetica', '10') if os.name == 'nt': font = ('MS Sans Serif', '8') elif name in ('canvas_large',): font = ('Helvetica', '-18') if os.name == 'nt': font = ('Arial', '14') elif name in ('canvas_card',): if cardw >= 71: font = ('Helvetica', '-18') if os.name == 'nt': font = ('Arial', '14') elif cardw >= 57: font = ('Helvetica', '-16') else: font = ('Helvetica', '-14') elif name in ('canvas_fixed',): font = ('Courier', '-12') if os.name == 'nt': font = ('Courier New', '9') elif os.name == 'mac': font = ('Monaco', '9') elif name in ('fixed',): font = ('Courier', '-14') if os.name == 'nt': font = ('Courier New', '10') elif os.name == 'mac': font = ('Courier', '12') elif not (name in ('default',)): pass getFont_cache[key] = font return font def loadImage(file): return Tkinter.PhotoImage(file = file) def copyImage(image, x, y, width, height): dest = Tkinter.PhotoImage(width = width, height = height) if not __debug__ and dest.width() == width: raise AssertionError if not __debug__ and dest.height() == height: raise AssertionError dest.blank() image.tk.call(dest, 'copy', image.name, '-from', x, y, x + width, y + height) if not __debug__ and dest.width() == width: raise AssertionError if not __debug__ and dest.height() == height: raise AssertionError return dest def fillImage(image, fill, outline = None): if not fill and not outline: return None width = image.width() height = image.height() ow = 1 if width <= 2 * ow or height <= 2 * ow: if not fill: pass fill = outline outline = None if not outline: f = (fill,) * width f = (f,) * height if not __debug__ and len(f) == height: raise AssertionError image.put(f) elif not fill: l = ((outline,) * width,) for y in range(0, ow): image.put(l, (0, y)) for y in range(height - ow, height): image.put(l, (0, y)) p = ((outline,) * ow,) for y in range(ow, height - ow): image.put(p, (0, y)) image.put(p, (width - ow, y)) else: l1 = (outline,) * width l2 = (outline,) * ow + (fill,) * (width - 2 * ow) + (outline,) * ow f = (l1,) * ow + (l2,) * (height - 2 * ow) + (l1,) * ow if not __debug__ and len(f) == height: raise AssertionError range(ow, height - ow) image.put(f) def createImage(width, height, fill, outline = None): image = Tkinter.PhotoImage(width = width, height = height) if not __debug__ and image.width() == width: raise AssertionError if not __debug__ and image.height() == height: raise AssertionError image.blank() fillImage(image, fill, outline) return image class MfxCanvasGroup(Canvas.Group): def __init__(_, canvas, tag = None): Canvas.Group.__init__(_, canvas = canvas, tag = tag) if not __debug__ and not _.canvas.items.has_key(_.id): raise AssertionError _.canvas.items[_.id] = _ def addtag(_, tag, option = 'withtag'): _.canvas.addtag(tag, option, _.id) def delete(_): del _.canvas.items[_.id] Canvas.Group.delete(_) def gettags(_): return _.canvas.tk.splitlist(_._do('gettags')) class MfxCanvasImage(Canvas.ImageItem): def moveTo(_, x, y): c = _.coords() _.move(x - int(c[0]), y - int(c[1])) MfxCanvasLine = Canvas.Line MfxCanvasRectangle = Canvas.Rectangle class MfxCanvasText(Canvas.CanvasText): def __init__(_, canvas, x, y, preview = -1, **kw): if preview < 0: preview = canvas.preview if preview > 1: return None if not kw.has_key('fill'): kw['fill'] = canvas._text_color apply(Canvas.CanvasText.__init__, (_, canvas, x, y), kw) _.text_format = None canvas._text_items.append(_) class MfxCanvas(Tkinter.Canvas): def __init__(_, *args, **kw): apply(Tkinter.Canvas.__init__, (_,) + args, kw) _.preview = 0 _.items = { } _._MfxCanvas__tileimage = None _._MfxCanvas__tiles = [] _._MfxCanvas__topimage = None _._MfxCanvas__tops = [] _._text_color = '#000000' _._text_items = [] def _x_create(_, itemType, *args, **kw): return Tkinter.Canvas._create(_, itemType, args, kw) def _create(_, itemType, args, kw): id = Tkinter.Canvas._create(_, itemType, args, kw) if _._MfxCanvas__tops: _.tk.call(_._w, 'lower', id, _._MfxCanvas__tops[0]) return id def tag_raise(_, id, aboveThis = None): if aboveThis is None and _._MfxCanvas__tops: _.tk.call(_._w, 'lower', id, _._MfxCanvas__tops[0]) else: _.tk.call(_._w, 'raise', id, aboveThis) def tag_lower(_, id, belowThis = None): if belowThis is None and _._MfxCanvas__tiles: _.tk.call(_._w, 'raise', id, _._MfxCanvas__tiles[-1]) else: _.tk.call(_._w, 'lower', id, belowThis) def deleteAllItems(_): _._text_items = [] for id in _.items.keys(): if not __debug__ and not (id in _._MfxCanvas__tiles): raise AssertionError 0 unbind_destroy(_.items[id]) _.items[id].delete() if not __debug__ and _.items == { }: raise AssertionError _.items.keys() def findCard(_, stack, event): return -1 def setTextColor(_, color): pass def setTile(_, image): try: if image and type(image) is types.StringType: image = Tkinter.PhotoImage(file = image) except Tkinter.TclError: return 0 for id in _._MfxCanvas__tiles: _.delete(id) _._MfxCanvas__tiles = [] _._MfxCanvas__tileimage = image (iw, ih) = (image.width(), image.height()) sw = max(_.winfo_screenwidth(), 1024) sh = max(_.winfo_screenheight(), 768) for x in range(0, sw - 1, iw): for y in range(0, sh - 1, ih): id = _._x_create('image', x, y, image = image, anchor = 'nw') _.tag_lower(id) _._MfxCanvas__tiles.append(id) return 1 def setTopImage(_, image, cw = 0, ch = 0): try: if image and type(image) is types.StringType: image = Tkinter.PhotoImage(file = image) except Tkinter.TclError: return 0 for id in _._MfxCanvas__tops: _.delete(id) _._MfxCanvas__tops = [] _._MfxCanvas__topimage = image (iw, ih) = (image.width(), image.height()) if cw <= 0: cw = int(_.cget('width')) if ch <= 0: ch = int(_.cget('height')) x = (cw - iw) / 2 y = (ch - ih) / 2 id = _._x_create('image', x, y, image = image, anchor = 'nw') _.tk.call(_._w, 'raise', id) _._MfxCanvas__tops.append(id) return 1 def _bind(_, what, sequence, func, add, needcleanup = 1): funcid = _._register(func, _._substitute, needcleanup) if not add and '+': pass cmd = '%sif {"[%s %s]" == "break"} break\n' % ('', funcid, '%x %y') _.tk.call(what + (sequence, cmd)) return funcid def _substitute(_, *args): e = Tkinter.Event() e.x = int(args[0]) e.y = int(args[1]) return (e,) class MfxCheckMenuItem(Tkinter.BooleanVar): def __init__(_, menubar, path = None): Tkinter.BooleanVar.__init__(_) def set(_, value): if not value or value == 'false': value = 0 if __debug__: if type(value) is types.IntType: if value <= value: pass elif not value <= 1: raise AssertionError Tkinter.BooleanVar.set(_, value) class MfxRadioMenuItem(Tkinter.IntVar): def __init__(_, menubar, path = None): Tkinter.IntVar.__init__(_) def set(_, value): if __debug__: if not type(value) is types.IntType and 0 <= value: raise AssertionError Tkinter.IntVar.set(_, value) class MfxRoot(Tkinter.Tk): def __init__(_, **kw): apply(Tkinter.Tk.__init__, (_,), kw) _.app = None def connectApp(_, app): _.app = app def busyUpdate(_): game = None if _.app: game = _.app.game if not game: _.update() else: old_busy = game.busy game.busy = 1 if game.canvas: game.canvas.update() _.update() game.busy = old_busy def mainquit(_): _.after_idle(_.quit) def screenshot(_, filename): pass def setCursor(_, cursor): pass def sleep(_, seconds): time.sleep(seconds) def update(_): Tkinter.Tk.update(_) def wmDeleteWindow(_): if _.app and _.app.menubar: _.app.menubar.mQuit() class _ToplevelDialog: img = None def __init__(_, parent, title = '', resizable = 0, default = -1): _.parent = parent _.status = 0 _.button = default _.timer = None _.top = makeToplevel(parent, title = title) _.top.wm_resizable(resizable, resizable) bind(_.top, 'WM_DELETE_WINDOW', _.wmDeleteWindow) if _.img is None: _ToplevelDialog.img = (Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPICAH8AAH9/f7+/v/8AAP///wAAAAAAAAAAACH5BAEAAAIALAAAAAAgACAAAAPZ\nKLos8PC1SRccOGNYO9AgCHTNE57ZQwoA6mZAB7z0AFRA/QLBBISEFyEECDAAIYISpSSEAAwAqKkM\nUQkgQGABCF011xAgEBAAUFfMFQUICACu69UFCAgAL7nyBSADaHI0AGQANHI0AGQAL3oELwBkAC6N\nSi4AZAAoVxhXKABkAgAhVxpXIQBkAQIAIFchVyAAqQIAIU0oTSEAZAsAIQQvBCEAqQwAOi4AqQEM\nAMgoAKkTAM8gAKkVD9UPyx0BAMgAywErARAoEOMrC2QREeMB6w3w9AHyFfUrCQA7'), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAAAA/39/f7+/v////wAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPU\nOLo88vC1SRccJOsBa32DJo7D4zHCMK7jIJyDwA5BHQws4AksYds9AEXQI/xqRUBDMCgOfoPiAMAQ\nFK8sgEAhGGC/mgFAMBBcjzUsQDAQfI9YAOR7xAIg3yMWAPkesQAQA1hHVwMADwMCWEdXABADAgNX\nR0UDABAKAj1oNiwAEAwAYEUAEQ0ApCsAERQAA6oEAwARHgAAA18DtxEwArcAAysDwBECMAoQwAMa\nAMYPyAwRABoAxtEVAgAaAA/YyAAZAA/fyAAEAA/lyLcP68gQ78gPMAkAOw=='), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAAAA/39/f7+/v////wAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPp\nOLo88vC1SRccJOsBa32DJo7D4zHCMK7jIJyDwM4j4AnsEOz8wAIUAWsQGGR4gQEL0BAMWDsRkjUA\nMAQzZAY5AwgUgsEMSRggZwOAYCCgDXYDApJGAAgGArpmwNMDIHoZPIEAEIEEOwOBABCHO4cAEAOH\nhwMADwMCegM7A3oAEAMCA3RIdAMAEAoCdEh0ABAMADQDOwM0ABENAJQrABEUAAO9BAMAER4AAAOB\nA8oRMALKAAMrA9MRAjAKENMDGgDZD9sMEQAaANnkFQIAGgAP69sAGQAP8tsABAAP+NvKD/xtgyBw\n2wMYCQAAOw=='), Tkinter.PhotoImage(data = '\nR0lGODlhIAAgAPIDAAAAAH9/AH9/f7+/v///AAAAAAAAAAAAACH5BAEAAAMALAAAAAAgACAAAAPu\nOLq88S3KqQIhA9Atw70DIHDkEHwXIAglFaDXAKytFMAXsNZNgF8DwIq3CPwugBVxEDheBoAVMfAZ\nAK6DD2DFC6Cu4M8AsKoFUOArCrBqBWBpAGoAWJUCsDgMsCIFcHEwAwArHAE4AWABOAArGwE/A2AD\nOAMAKxQBR2BHACsTAU4BAAFHAwArEgFOAwADTgArEQFOFwC1AwArDQG1vhcAKwwBvgNXA04DACsL\nAb5ptQArCgG/abUDACsDAb8DVwO+ACsDAb/nFwArAgHovwMA6wMB7k4DAOsCCgFx/f79+QQwCEiw\noEEBEQ4qJKggAQA7')) def mainloop(_, focus = None, timeout = 0): bind(_.top, '<Escape>', _.mCancel) if focus is not None: focus.focus() setTransient(_.top, _.parent) try: _.top.grab_set() except Tkinter.TclError: pass if timeout > 0: _.timer = after(_.top, timeout, _.mTimeout) try: _.top.mainloop() except SystemExit: pass _.destroy() def destroy(_): after_cancel(_.timer) unbind_destroy(_.top) try: _.top.wm_withdraw() except: if traceback: traceback.print_exc() try: _.top.destroy() except: if traceback: traceback.print_exc() destruct(_.top) if _.parent: try: if hasattr(_.parent, 'busyUpdate'): _.parent.busyUpdate() else: _.parent.update() except: if traceback: traceback.print_exc() _.top = None _.parent = None def getDefaultFont(_): return getFont('default') def wmDeleteWindow(_, *event): _.status = 1 raise SystemExit def mCancel(_, *event): _.status = 1 raise SystemExit def mTimeout(_, *event): _.status = 2 raise SystemExit class MfxDialog(_ToplevelDialog): def __init__(_, parent, title, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.button = kw.default msg = Tkinter.Label(top_frame, text = kw.text, justify = kw.justify, width = kw.width, font = kw.font) msg.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady) focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, timeout = 0, resizable = 0, text = '', justify = 'center', strings = ('OK',), default = 0, width = 0, font = _.getDefaultFont(), buttonfont = _.getDefaultFont(), padx = 20, pady = 20, bitmap = None, bitmap_side = 'left', bitmap_padx = 10, bitmap_pady = 20, image = None, image_side = 'left', image_padx = 10, image_pady = 20) sw = 2 * (len(kw.strings) > 1) kwdefault(kw.__dict__, separatorwidth = sw) return kw def createFrames(_, kw): frame = Tkinter.Frame(_.top) frame.pack(side = Tkinter.TOP, fill = Tkinter.BOTH, expand = 1) if kw.separatorwidth > 0: separator = Tkinter.Frame(_.top, relief = 'sunken', height = kw.separatorwidth, width = kw.separatorwidth, borderwidth = kw.separatorwidth / 2) separator.pack(side = Tkinter.TOP, fill = Tkinter.X) bot = Tkinter.Frame(_.top) bot.pack(side = Tkinter.BOTTOM, fill = Tkinter.BOTH, ipady = 3) return (frame, bot) def createBitmaps(_, frame, kw): bm = [ 'error', 'info', 'questhead', 'warning'] if kw.bitmap in bm: b = Tkinter.Label(frame, image = _.img[bm.index(kw.bitmap)]) b.pack(side = kw.bitmap_side, padx = kw.bitmap_padx, pady = kw.bitmap_pady) elif kw.bitmap: b = Tkinter.Label(frame, bitmap = kw.bitmap) b.pack(side = kw.bitmap_side, padx = kw.bitmap_padx, pady = kw.bitmap_pady) elif kw.image: b = Tkinter.Label(frame, image = kw.image) b.pack(side = kw.image_side, padx = kw.image_padx, pady = kw.image_pady) def createButtons(_, frame, kw): button = column = -1 (padx, pady) = (kw.get('buttonpadx', 10), kw.get('buttonpady', 10)) focus = None max_len = 6 for s in kw.strings: if s: s = re.sub('[\\s\\.\\,]', '', s) max_len = max(max_len, len(s)) for s in kw.strings: if s is None: continue if button < 0: b = Tkinter.Button(frame, text = s, font = kw.buttonfont, state = 'disabled') button = xbutton else: b = Tkinter.Button(frame, text = s, font = kw.buttonfont, default = 'normal', command = (lambda _ = _, button = button: _.mDone(button))) if button == kw.default: focus = b focus.config(default = 'active') l = len(s) if 1 and l < max_len: l = l + (max_len - l) / 2 b.config(width = l) column = column + 1 b.grid_configure(column = column, row = 0, sticky = 'ew', padx = padx, pady = pady) b.grid_columnconfigure(column) if focus is not None: l = lambda event = None, _ = _, button = kw.default: _.mDone(button) bind(_.top, '<Return>', l) bind(_.top, '<KP_Enter>', l) return focus def mDone(_, button): _.button = button raise SystemExit class MfxExceptionDialog(MfxDialog): def __init__(_, parent, ex, title = 'Error', **kw): kw = KwStruct(kw, bitmap = 'error') text = str(kw.get('text', '')) if text and text[-1] != '\n': text = text + '\n' text = text + '\n' if isinstance(ex, EnvironmentError) and ex.filename is not None: t = '[Errno %s] %s:\n%s' % (ex.errno, ex.strerror, repr(ex.filename)) else: t = str(ex) kw.text = text + t apply(MfxDialog.__init__, (_, parent, title), kw.getKw()) class MfxSimpleSlider(MfxDialog): def __init__(_, parent, title, label, value, from_, to, resolution, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.value = value _.var = Tkinter.DoubleVar() _.var.set(value) slider = Tkinter.Scale(top_frame, from_ = from_, to = to, resolution = resolution, orient = Tkinter.HORIZONTAL, length = '3i', label = label, variable = _.var, takefocus = 0) slider.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 0) return MfxDialog.initKw(_, kw) def mDone(_, button): _.button = button _.value = _.var.get() raise SystemExit class MfxSimpleEntry(MfxSimpleSlider): def __init__(_, parent, title, label, value, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.value = value if label: label = Tkinter.Label(top_frame, text = label, takefocus = 0) label.pack(pady = 5) w = kw.get('e_width', 0) _.var = Tkinter.Entry(top_frame, exportselection = 1, width = w) _.var.insert(0, value) _.var.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) focus = _.createButtons(bottom_frame, kw) focus = _.var _.mainloop(focus, kw.timeout) class MfxTooltip: def __init__(_, widget): _.widget = widget _.text = None _.timer = None _.tooltip = None _.label = None _.bindings = [] _.bindings.append(_.widget.bind('<Enter>', _._enter)) _.bindings.append(_.widget.bind('<Leave>', _._leave)) _.bindings.append(_.widget.bind('<ButtonPress>', _._leave)) _.time = 1000 _.relief = Tkinter.SOLID _.justify = Tkinter.LEFT _.fg = '#000000' _.bg = '#ffffe0' _.xoffset = 20 _.yoffset = 1 def setText(_, text): _.text = text def _unbind(_): if _.bindings and _.widget: _.widget.unbind('<Enter>', _.bindings[0]) _.widget.unbind('<Leave>', _.bindings[1]) _.widget.unbind('<ButtonPress>', _.bindings[2]) _.bindings = [] def destroy(_): _._unbind() _._leave() def _enter(_, *event): after_cancel(_.timer) _.timer = after(_.widget, _.time, _._showTip) def _leave(_, *event): after_cancel(_.timer) _.timer = None if _.tooltip: _.label.destroy() destruct(_.label) _.label = None _.tooltip.destroy() destruct(_.tooltip) _.tooltip = None def _showTip(_): if _.tooltip or not (_.text): return None c = _.widget.__class__ if c in (Tkinter.Button,): if _.widget['state'] == Tkinter.DISABLED: return None x = _.widget.winfo_rootx() y = _.widget.winfo_rooty() + _.widget.winfo_height() x = x + _.xoffset y = y + _.yoffset _.tooltip = Tkinter.Toplevel() _.tooltip.wm_iconify() _.tooltip.wm_overrideredirect(1) _.tooltip.wm_protocol('WM_DELETE_WINDOW', _.destroy) _.label = Tkinter.Label(_.tooltip, text = _.text, relief = _.relief, justify = _.justify, fg = _.fg, bg = _.bg, bd = 1, takefocus = 0) _.label.pack(ipadx = 1, ipady = 1) if os.name == 'nt': pass need_fix = tkversion < (8, 3, 0, 0) if not need_fix: _.tooltip.wm_geometry('%+d%+d' % (x, y)) _.tooltip.wm_deiconify() if need_fix: _.tooltip.wm_geometry('%+d%+d' % (x, y)) class MfxScrolledCanvas: def __init__(_, parent, vbar = 1, hbar = 0, **kw): bg = kw.get('bg', parent.cget('bg')) kwdefault(kw, bg = bg, highlightthickness = 0, xscrollincrement = 16, yscrollincrement = 16) _.parent = parent _.frame = Tkinter.Frame(parent, bg = bg) _.frame.grid_rowconfigure(0, weight = 1) _.frame.grid_columnconfigure(0, weight = 1) _.canvas = None _.vbar = None _.vbar_mode = vbar _.hbar = None _.hbar_mode = hbar _.createCanvas(kw) if vbar: _.createVbar(bg) if hbar: _.createHbar(bg) _.canvas.focus_set() def unbind(_): unbind_destroy(_.canvas) unbind_destroy(_.frame) def destroy(_): _.unbind() def createCanvas(_, kw): _.canvas = apply(Tkinter.Canvas, (_.frame,), kw) _.canvas.grid(row = 0, column = 0, sticky = 'nsew') def createVbar(_, bg): _.vbar = Tkinter.Scrollbar(_.frame, name = 'vbar', bg = bg, takefocus = 0) _.canvas['yscrollcommand'] = _.vbar.set _.vbar['command'] = _.canvas.yview w = _.canvas bind(w, '<KeyPress-Prior>', _.page_up) bind(w, '<KeyPress-Next>', _.page_down) bind(w, '<KeyPress-Up>', _.unit_up) bind(w, '<KeyPress-Down>', _.unit_down) bind(w, '<KeyPress-Begin>', _.scroll_top) bind(w, '<KeyPress-Home>', _.scroll_top) bind(w, '<KeyPress-End>', _.scroll_bottom) def createHbar(_, bg): _.hbar = Tkinter.Scrollbar(_.frame, name = 'hbar', bg = bg, takefocus = 0, orient = 'horizontal') _.canvas['xscrollcommand'] = _.hbar.set _.hbar['command'] = _.canvas.xview w = _.canvas bind(w, '<KeyPress-Left>', _.unit_left) bind(w, '<KeyPress-Right>', _.unit_right) def showVbar(_, show = -1): if not (_.vbar): return None if show < 0: show = _.vbar_mode if show > 1: view = _.canvas.yview() if not abs(view[0]) > 0.0001: pass show = abs(view[1] - 1.0) > 0.0001 if show: _.vbar.grid(row = 0, column = 1, sticky = 'nse') _.frame.grid_columnconfigure(1, minsize = 0) else: w = 21 b = _.frame.grid_bbox(row = 0, column = 2) if b and len(b) == 4 and b[2] > 0 and b[3] > 0: w = b[2] _.vbar.grid_forget() _.frame.grid_columnconfigure(1, minsize = w) def showHbar(_, show = -1): if not (_.hbar): return None if show < 0: show = _.hbar_mode if show > 1: view = _.canvas.xview() if not abs(view[0]) > 0.0001: pass show = abs(view[1] - 1.0) > 0.0001 if show: _.hbar.grid(row = 1, column = 0, sticky = 'ews') _.frame.grid_rowconfigure(1, minsize = 0) else: w = 21 b = _.frame.grid_bbox(row = 2, column = 0) if b and len(b) == 4 and b[2] > 0 and b[3] > 0: w = b[3] _.hbar.grid_forget() _.frame.grid_rowconfigure(1, minsize = w) def page_up(_, *event): _.canvas.yview_scroll(-1, 'page') return 'break' def page_down(_, *event): _.canvas.yview_scroll(1, 'page') return 'break' def unit_up(_, *event): _.canvas.yview_scroll(-1, 'unit') return 'break' def unit_down(_, *event): _.canvas.yview_scroll(1, 'unit') return 'break' def page_left(_, *event): _.canvas.xview_scroll(-1, 'page') return 'break' def page_right(_, *event): _.canvas.xview_scroll(1, 'page') return 'break' def unit_left(_, *event): _.canvas.xview_scroll(-1, 'unit') return 'break' def unit_right(_, *event): _.canvas.xview_scroll(1, 'unit') return 'break' def scroll_top(_, *event): _.canvas.yview_moveto(0) return 'break' def scroll_bottom(_, *event): _.canvas.yview_moveto(1) return 'break' class MfxScrolledText(Tkinter.Text): def __init__(_, parent = None, **cnf): fcnf = { } for k in cnf.keys(): pass if cnf.has_key('bg'): fcnf['bg'] = cnf['bg'] _.frame = apply(Tkinter.Frame, (parent,), fcnf) _.vbar = Tkinter.Scrollbar(_.frame, name = 'vbar') _.vbar.pack(side = Tkinter.RIGHT, fill = Tkinter.Y) cnf['name'] = 'text' apply(Tkinter.Text.__init__, (_, _.frame), cnf) _.pack(side = Tkinter.LEFT, fill = Tkinter.BOTH, expand = 1) _['yscrollcommand'] = _.vbar.set _.vbar['command'] = _.yview for m in Tkinter.Pack.__dict__.keys(): pass _.frame['highlightthickness'] = 0 _.vbar['highlightthickness'] = 0 def xview_moveto(_, fraction): return _.tk.call(_._w, 'xview', 'moveto', fraction) def xview_scroll(_, number, what): return _.tk.call(_._w, 'xview', 'scroll', number, what) def yview_moveto(_, fraction): return _.tk.call(_._w, 'yview', 'moveto', fraction) def yview_scroll(_, number, what): return _.tk.call(_._w, 'yview', 'scroll', number, what) class MfxReadonlyScrolledText(MfxScrolledText): def __init__(_, parent = None, **cnf): apply(MfxScrolledText.__init__, (_, parent), cnf) _.config(state = 'disabled', insertofftime = 0) _.frame.config(takefocus = 0) _.config(takefocus = 0) _.vbar.config(takefocus = 0) class tkHTMLWriter(formatter.DumbWriter): def __init__(_, text, viewer): formatter.DumbWriter.__init__(_, _, maxcol = 9999) _.text = text _.viewer = viewer (font, size) = ('Helvetica', 12) f = _.text['font'] if f[0] == '{': m = re.search('^\\{([^\\}]+)\\}\\s*(-?\\d+)', f) if m: (font, size) = (m.group(1), int(m.group(2))) else: f = string.split(f) (font, size) = (f[0], int(f[1])) sign = 1 if size < 0: sign = -1 fixed = ('Courier', 12) if os.name == 'nt': fixed = ('Courier New', 10) _.fontmap = { 'h1': (font, size + 12 * sign, 'bold'), 'h2': (font, size + 8 * sign, 'bold'), 'h3': (font, size + 6 * sign, 'bold'), 'h4': (font, size + 4 * sign, 'bold'), 'h5': (font, size + 2 * sign, 'bold'), 'h6': (font, size + 1 * sign, 'bold'), 'bold': (font, size, 'bold'), 'italic': (font, size, 'italic'), 'pre': fixed } _.text.config(cursor = _.viewer.defcursor) for f in _.fontmap.keys(): _.text.tag_config(f, font = _.fontmap[f]) _.anchor = None _.anchor_mark = None _.font = None _.font_mark = None _.indent = '' def createCallback(_, href): class Functor: def __init__(_, viewer, arg): _.viewer = viewer _.arg = arg def __call__(_, *args): _.viewer.updateHistoryXYView() return _.viewer.display(_.arg) return Functor(_.viewer, href) def write(_, data): _.text.insert('insert', data) def __write(_, data): _.text.insert('insert', data) def anchor_bgn(_, href, name, type): if href: _.anchor = (href, name, type) _.anchor_mark = _.text.index('insert') def anchor_end(_): if _.anchor: url = _.anchor[0] tag = 'href_' + url _.text.tag_add(tag, _.anchor_mark, 'insert') _.text.tag_bind(tag, '<ButtonPress>', _.createCallback(url)) _.text.tag_bind(tag, '<Enter>', _.anchor_enter) _.text.tag_bind(tag, '<Leave>', _.anchor_leave) _.text.tag_config(tag, foreground = 'blue', underline = 1) _.anchor = None def anchor_enter(_, *args): _.text.config(cursor = _.viewer.handcursor) def anchor_leave(_, *args): _.text.config(cursor = _.viewer.defcursor) def new_font(_, font): if _.font: _.text.tag_add(_.font, _.font_mark, 'insert') _.font = None if font: _.font_mark = _.text.index('insert') if _.fontmap.has_key(font[0]): _.font = font[0] elif font[3]: _.font = 'pre' elif font[2]: _.font = 'bold' elif font[1]: _.font = 'italic' else: _.font = None def new_margin(_, margin, level): _.indent = ' ' * level def send_label_data(_, data): _._tkHTMLWriter__write(_.indent + data + ' ') def send_paragraph(_, blankline): if _.col > 0: _._tkHTMLWriter__write('\n') if blankline > 0: _._tkHTMLWriter__write('\n' * blankline) _.col = 0 _.atbreak = 0 def send_hor_rule(_, *args): width = int(int(_.text['width']) * 0.9) _._tkHTMLWriter__write('_' * width) _._tkHTMLWriter__write('\n') _.col = 0 _.atbreak = 0 class tkHTMLParser(htmllib.HTMLParser): def anchor_bgn(_, href, name, type): htmllib.HTMLParser.anchor_bgn(_, href, name, type) _.formatter.writer.anchor_bgn(href, name, type) def anchor_end(_): if _.anchor: _.anchor = None _.formatter.writer.anchor_end() def do_dt(_, attrs): _.formatter.end_paragraph(1) _.ddpop() def handle_image(_, src, alt, ismap, align, width, height): _.formatter.writer.viewer.showImage(src, alt, ismap, align, width, height) class tkHTMLViewer: def __init__(_, parent): _.parent = parent _.home = None _.url = None _.history = Struct(list = [], index = 0) _.images = [] _.defcursor = parent['cursor'] _.handcursor = 'hand2' frame = _.frame = Tkinter.Frame(parent) frame.pack(side = 'bottom', fill = 'x') _.homeButton = Tkinter.Button(frame, text = 'Index', command = _.goHome) _.homeButton.pack(side = 'left') _.backButton = Tkinter.Button(frame, text = 'Back', command = _.goBack) _.backButton.pack(side = 'left') _.forwardButton = Tkinter.Button(frame, text = 'Forward', command = _.goForward) _.forwardButton.pack(side = 'left') _.closeButton = Tkinter.Button(frame, text = 'Close', command = _.destroy) _.closeButton.pack(side = 'right') basefont = ('Helvetica', 12) if os.name == 'nt': basefont = ('Times New Roman', 12) _.text = MfxReadonlyScrolledText(parent, fg = '#000000', bg = '#f7f3ff', cursor = _.defcursor, font = basefont, wrap = 'word', padx = 20, pady = 20) _.text.pack(side = 'top', fill = 'both', expand = 1) _.initBindings() def initBindings(_): w = _.parent bind(w, 'WM_DELETE_WINDOW', _.destroy) bind(w, '<Escape>', _.destroy) bind(w, '<KeyPress-Prior>', _.page_up) bind(w, '<KeyPress-Next>', _.page_down) bind(w, '<KeyPress-Up>', _.unit_up) bind(w, '<KeyPress-Down>', _.unit_down) bind(w, '<KeyPress-Begin>', _.scroll_top) bind(w, '<KeyPress-Home>', _.scroll_top) bind(w, '<KeyPress-End>', _.scroll_bottom) bind(w, '<KeyPress-BackSpace>', _.goBack) def destroy(_, *event): unbind_destroy(_.parent) try: _.parent.wm_withdraw() except: pass try: _.parent.destroy() except: pass _.parent = None def page_up(_, *event): _.text.yview_scroll(-1, 'page') return 'break' def page_down(_, *event): _.text.yview_scroll(1, 'page') return 'break' def unit_up(_, *event): _.text.yview_scroll(-1, 'unit') return 'break' def unit_down(_, *event): _.text.yview_scroll(1, 'unit') return 'break' def scroll_top(_, *event): _.text.yview_moveto(0) return 'break' def scroll_bottom(_, *event): _.text.yview_moveto(1) return 'break' def basejoin(_, url, baseurl = None, relpath = 1): if baseurl is None: baseurl = _.url url = os.path.normpath(url) if relpath and baseurl and not os.path.isabs(url): (h1, t1) = os.path.split(url) (h2, t2) = os.path.split(baseurl) if cmp(h1, h2) != 0: url = os.path.join(h2, h1, t1) url = os.path.normpath(url) return url def display(_, url, add = 1, relpath = 1, xview = 0, yview = 0): if _.__dict__.get('app'): if _.app and _.app.game: _.app.game._cancelDrag() for p in ('ftp:', 'gopher:', 'http:', 'mailto:', 'news:', 'telnet:'): if string.find(url, p) != -1: return None url = _.basejoin(url, relpath = relpath) try: file = None if os.path.isdir(url): url = os.path.join(url, 'index.html') url = os.path.normpath(url) file = open(url) data = file.read() file.close() file = None except Exception: ex = None if file: file.close() _.errorDialog('Unable to service request:\n' + url + '\n\n' + str(ex)) return None except: if file: file.close() _.errorDialog('Unable to service request:\n' + url) return None _.url = url if _.home is None: _.home = _.url if add: _.addHistory(_.url, xview = xview, yview = yview) if _.history.index > 1: _.backButton.config(state = 'normal') else: _.backButton.config(state = 'disabled') if _.history.index < len(_.history.list): _.forwardButton.config(state = 'normal') else: _.forwardButton.config(state = 'disabled') (old_c1, old_c2) = (_.defcursor, _.handcursor) _.defcursor = _.handcursor = 'watch' _.text.config(cursor = _.defcursor) _.text.update_idletasks() _.frame.config(cursor = _.defcursor) _.frame.update_idletasks() _.text.config(state = 'normal') _.text.delete('1.0', 'end') _.images = [] writer = tkHTMLWriter(_.text, _) fmt = formatter.AbstractFormatter(writer) parser = tkHTMLParser(fmt) parser.feed(data) parser.close() _.text.config(state = 'disabled') if xview <= xview: pass elif xview <= 1.0: _.text.xview_moveto(xview) if yview <= yview: pass elif yview <= 1.0: _.text.yview_moveto(yview) _.parent.wm_title(parser.title) _.parent.wm_iconname(parser.title) (_.defcursor, _.handcursor) = (old_c1, old_c2) _.text.config(cursor = _.defcursor) _.frame.config(cursor = _.defcursor) def addHistory(_, url, xview = 0, yview = 0): if _.history.index > 0: (u, xv, yv) = _.history.list[_.history.index - 1] if cmp(u, url) == 0: _.updateHistoryXYView() return None del _.history.list[_.history.index:] _.history.list.append((url, xview, yview)) _.history.index = _.history.index + 1 def updateHistoryXYView(_): if _.history.index > 0: (url, xview, yview) = _.history.list[_.history.index - 1] xview = _.text.xview()[0] yview = _.text.yview()[0] _.history.list[_.history.index - 1] = (url, xview, yview) def goBack(_, *event): if _.history.index > 1: _.updateHistoryXYView() _.history.index = _.history.index - 1 (url, xview, yview) = _.history.list[_.history.index - 1] _.display(url, add = 0, relpath = 0, xview = xview, yview = yview) def goForward(_, *event): if _.history.index < len(_.history.list): _.updateHistoryXYView() (url, xview, yview) = _.history.list[_.history.index] _.history.index = _.history.index + 1 _.display(url, add = 0, relpath = 0, xview = xview, yview = yview) def goHome(_, *event): if _.home and cmp(_.home, _.url) != 0: _.updateHistoryXYView() _.display(_.home, relpath = 0) def errorDialog(_, msg): d = MfxDialog(_.parent, title = PACKAGE + ' HTML Problem', text = msg, bitmap = 'warning', strings = ('OK',), default = 0) def showImage(_, src, alt, ismap, align, width, height): url = _.basejoin(src) try: img = Tkinter.PhotoImage(file = url) except Tkinter.TclError: img = None except: img = None if img: (padx, pady) = (10, 10) (padx, pady) = (0, 20) if string.lower(align) == 'left': padx = 0 _.text.image_create(index = 'insert', image = img, padx = padx, pady = pady) _.images.append(img) class DisplayTextDialog(MfxDialog): Text_Class = MfxReadonlyScrolledText def __init__(_, parent, title, text, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) bg = top_frame['bg'] _.text_w = _.Text_Class(top_frame, bd = 1, relief = 'sunken', wrap = 'word', width = 80, height = 30, bg = bg) _.text = '' if text: _.text = str(text) old_state = _.text_w['state'] _.text_w.config(state = 'normal') _.text_w.insert('insert', _.text) _.text_w.config(state = old_state) _.text_w.pack(side = 'top', fill = 'both', expand = 1) focus = _.createButtons(bottom_frame, kw) focus = _.text_w _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK',), default = 0, resizable = 1, separatorwidth = 0) return MfxDialog.initKw(_, kw) class EditTextDialog(DisplayTextDialog): Text_Class = MfxScrolledText def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = -1, resizable = 1, separatorwidth = 0) return MfxDialog.initKw(_, kw) def destroy(_): _.text = _.text_w.get('1.0', 'end') DisplayTextDialog.destroy(_) def wmDeleteWindow(_, *event): pass def mCancel(_, *event): pass class PlayerOptionsDialog(MfxDialog): def __init__(_, parent, title, app, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.update_stats_var = Tkinter.BooleanVar() _.update_stats_var.set(app.opt.update_player_stats != 0) _.confirm_var = Tkinter.BooleanVar() _.confirm_var.set(app.opt.confirm != 0) _.win_animation_var = Tkinter.BooleanVar() _.win_animation_var.set(app.opt.win_animation != 0) widget = Tkinter.Label(top_frame, text = '\nPlease enter your name', takefocus = 0) widget.pack(pady = 5) w = kw.get('e_width', 30) _.player_var = Tkinter.Entry(top_frame, exportselection = 1, width = w) _.player_var.insert(0, app.opt.player) _.player_var.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) widget = Tkinter.Checkbutton(top_frame, variable = _.confirm_var, text = 'Confirm quit') widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) widget = Tkinter.Checkbutton(top_frame, variable = _.update_stats_var, text = 'Update statistics and logs') widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) _.player = _.player_var.get() _.confirm = _.confirm_var.get() _.update_stats = _.update_stats_var.get() _.win_animation = _.win_animation_var.get() focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def mDone(_, button): _.button = button _.player = _.player_var.get() _.confirm = _.confirm_var.get() _.update_stats = _.update_stats_var.get() _.win_animation = _.win_animation_var.get() raise SystemExit def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, padx = 10, pady = 10) return MfxDialog.initKw(_, kw) class DemoOptionsDialog(MfxDialog): def __init__(_, parent, title, app, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.demo_logo_var = Tkinter.BooleanVar() _.demo_logo_var.set(app.opt.demo_logo != 0) _.demo_score_var = Tkinter.BooleanVar() _.demo_score_var.set(app.opt.demo_score != 0) _.demo_sleep_var = Tkinter.DoubleVar() _.demo_sleep_var.set(app.opt.demo_sleep) widget = Tkinter.Checkbutton(top_frame, variable = _.demo_logo_var, text = 'Display floating Demo logo') widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) widget = Tkinter.Checkbutton(top_frame, variable = _.demo_score_var, text = 'Show score in statusbar') widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) widget = Tkinter.Scale(top_frame, from_ = 0.2, to = 9.9, resolution = 0.1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Set demo delay in seconds', variable = _.demo_sleep_var, takefocus = 0) widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) _.demo_logo = _.demo_logo_var.get() _.demo_score = _.demo_score_var.get() _.demo_sleep = _.demo_sleep_var.get() def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Cancel'), default = 0, separatorwidth = 0) return MfxDialog.initKw(_, kw) class HintOptionsDialog: def __init__(_, parent, title, app, **kw): d = MfxSimpleSlider(parent, title, 'Set hint delay in seconds', app.opt.hint_sleep, 0.2, 9.9, 0.1) _.status = d.status _.button = d.button _.hint_sleep = d.value class SoundOptionsDialog(MfxDialog): MIXER = () def __init__(_, parent, title, app, **kw): _.app = app kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.saved_opt = app.opt.copy() _.sound = Tkinter.BooleanVar() _.sound.set(app.opt.sound != 0) _.sound_mode = Tkinter.BooleanVar() _.sound_mode.set(app.opt.sound_mode != 0) _.sample_volume = Tkinter.IntVar() _.sample_volume.set(app.opt.sound_sample_volume) _.music_volume = Tkinter.IntVar() _.music_volume.set(app.opt.sound_music_volume) widget = Tkinter.Checkbutton(top_frame, variable = _.sound, text = 'Sound enabled') widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) if os.name == 'nt' and pysolsoundserver: widget = Tkinter.Checkbutton(top_frame, variable = _.sound_mode, text = 'Use DirectX for sound playing', command = _.mOptSoundDirectX) widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) if pysolsoundserver and app.startup_opt.sound_mode > 0: widget = Tkinter.Scale(top_frame, from_ = 0, to = 128, resolution = 1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Sample volume', variable = _.sample_volume, takefocus = 0) widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) widget = Tkinter.Scale(top_frame, from_ = 0, to = 128, resolution = 1, orient = Tkinter.HORIZONTAL, length = '3i', label = 'Music volume', variable = _.music_volume, takefocus = 0) widget.pack(side = Tkinter.TOP, padx = kw.padx, pady = kw.pady) else: kw.strings[1] = None focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def initKw(_, kw): strings = [ 'OK', 'Apply', 'Mixer...', 'Cancel'] if _.MIXER is None: strings[2] = ('Mixer...', -1) kw = KwStruct(kw, strings = strings, default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5) return MfxDialog.initKw(_, kw) def mDone(_, button): if button == 0 or button == 1: _.app.opt.sound = _.sound.get() _.app.opt.sound_mode = _.sound_mode.get() _.app.opt.sound_sample_volume = _.sample_volume.get() _.app.opt.sound_music_volume = _.music_volume.get() elif button == 2: MIXERS = () if os.name == 'nt': MIXERS = (('sndvol32.exe', None),) elif os.name == 'posix': MIXERS = (('kmix', None), ('gmix', None)) for name, args in MIXERS: try: f = spawnvp(name, args) if f: _.MIXER = (f, args) return None except: 0 MIXERS if traceback: traceback.print_exc() _.MIXER = None elif button == 3: _.app.opt = _.saved_opt if _.app.audio: _.app.audio.updateSettings() if button == 1: _.app.audio.playSample('drop', priority = 1000) if button == 1: return EVENT_HANDLED return MfxDialog.mDone(_, button) def mCancel(_, *event): return _.mDone(2) def wmDeleteWindow(_, *event): return _.mDone(0) def mOptSoundDirectX(_, *event): d = MfxDialog(_.top, title = 'Sound info', text = 'Changing DirectX settings takes effect\nafter the next start of ' + PACKAGE, bitmap = 'info', default = 0, strings = ('OK',)) class AboutDialog(MfxDialog): def createFrames(_, kw): (top_frame, bottom_frame) = MfxDialog.createFrames(_, kw) return (top_frame, bottom_frame) def helpAbout(app, timeout = 0, sound = 1): if sound: app.audio.playSample('about') t = 'A Python Solitaire Game Collection\n' if app.miscrandom.random() < 0.8 and os.name == 'posix': t = 'A World Domination Project\n' if PACKAGE == 'PyJongg': t = 'A Python Mahjongg Game Collection\n' d = AboutDialog(app.top, title = 'About ' + PACKAGE, timeout = timeout, text = PACKAGE + '\n' + t + 'Version ' + VERSION + '\n\n' + 'Copyright (C) 1998, 1999, 2000 Markus F.X.J. Oberhumer\n' + 'All Rights Reserved.\n\n' + PACKAGE + ' is free software distributed under the terms\n' + 'of the GNU General Public License\n\n' + 'For more information about this application visit\n' + PACKAGE_URL, image = app.gimages.logos[2], strings = ('Nice', 'Credits...'), default = 0, separatorwidth = 2) if d.status == 0 and d.button == 1: helpCredits(app, sound = sound) return d.status def helpAboutSimple(app, timeout = 0, sound = 1): if sound: app.audio.playSample('about') t = 'A Solitaire Game Collection\n' if PACKAGE == 'PyJongg': t = 'A Python Mahjongg Game Collection\n' d = MfxDialog(app.top, title = 'About ' + PACKAGE, timeout = timeout, text = PACKAGE + '\n' + t + 'Version ' + VERSION + '\n\n' + 'Copyright (C) 1998, 1999, 2000\nMarkus F.X.J. Oberhumer\n' + '\nFor more information about this application visit\n' + PACKAGE_URL, image = app.gimages.logos[2], strings = ('Nice',), default = 0, separatorwidth = 2) return d.status def helpCredits(app, timeout = 0, sound = 1): if sound: app.audio.playSample('credits') t = '' if tkname == 'tk': t = 'Tcl/Tk, ' elif tkname == 'gnome': t = 'PyGTK, ' elif tkname == 'kde': t = 'pyKDE, ' elif tkname == 'wx': t = 'wxPython, ' d = MfxDialog(app.top, title = PACKAGE + ' Credits', timeout = timeout, text = PACKAGE + ' credits go to:\n\n' + 'Volker Weidner for getting me into Solitaire\n' + 'Guido van Rossum for the initial example program\n' + 'T. Kirk for lots of contributed games and cardsets\n' + 'Carl Larsson for the background music\n' + 'The Gnome AisleRiot team for parts of the documentation\n' + '\n' + 'The Python, ' + t + 'SDL & Linux crews\nfor making this program possible', image = app.gimages.logos[3], image_side = 'right', separatorwidth = 2) return d.status help_html_index = None def helpHTML(app, document, dir): global help_html_index if not document: return None try: doc = app.dataloader.findFile(document, dir) if help_html_index is None: (document, dir) = ('index.html', 'html') help_html_index = app.dataloader.findFile(document, dir) except EnvError: d = MfxDialog(app.top, title = PACKAGE + ' HTML Problem', text = 'Cannot find help document\n' + document, bitmap = 'warning') return None top = makeHelpToplevel(app.top, title = PACKAGE + ' Help') if top.winfo_screenwidth() < 800 or top.winfo_screenheight() < 600: maximized = 1 top.wm_minsize(300, 150) else: maximized = 0 top.wm_minsize(400, 200) try: wm_set_icon(top, app.dataloader.findIcon()) except: pass viewer = tkHTMLViewer(top) viewer.app = app viewer.home = help_html_index viewer.display(doc) wm_map(top, maximized = maximized) return viewer class Status_StatsDialog(MfxDialog): def __init__(_, parent, game): (stats, gstats) = (game.stats, game.gstats) w1 = w2 = '' n = 0 for s in game.s.foundations: n = n + len(s.cards) if PACKAGE == 'PyJongg': w1 = 'Highlight tiles: ' + str(stats.highlight_piles) + '\n' w2 = w2 + '\nTiles removed: ' + str(n) w2 = w2 + '\nTiles remaining: ' + str(len(game.cards) - n) else: w1 = 'Highlight piles: ' + str(stats.highlight_piles) + '\n' + 'Highlight cards: ' + str(stats.highlight_cards) + '\n' + 'Highlight same rank: ' + str(stats.highlight_samerank) + '\n' if game.s.waste and game.s.waste not in game.s.foundations: w2 = w2 + '\nCards in Waste: ' + str(len(game.s.waste.cards)) if game.s.foundations: w2 = w2 + '\nCards in Foundations: ' + str(n) date = time.strftime('%Y-%m-%d %H:%M', time.localtime(game.gstats.start_time)) MfxDialog.__init__(_, parent, title = 'Game status', text = game.getTitleName() + '\n' + game.getGameNumber(format = 1) + '\n' + 'Playing time: ' + game.getTime() + '\n' + 'Started at: ' + date + '\n\n' + 'Moves: ' + str(game.moves.index) + '\n' + 'Undo moves: ' + str(stats.undo_moves) + '\n' + 'Bookmark moves: ' + str(gstats.goto_bookmark_moves) + '\n' + 'Demo moves: ' + str(stats.demo_moves) + '\n\n' + 'Hints: ' + str(stats.hints) + '\n' + w1 + w2, strings = ('OK', ('Statistics...', 101)), image = game.app.gimages.logos[3], image_side = 'left', image_padx = 20, padx = 20, separatorwidth = 2) class PysolStatsFormatter: def __init__(_, app): _.app = app class StringWriter: def __init__(_): _.text = '' def p(_, s): _.text = _.text + s def nl(_, count = 1): _.p('\n' * count) def pheader(_, s): _.p(s) def pstats(_, t1, t2, t3, t4, t5, gameid = None): s = '%-30s %7s %7s %7s %7s\n' % (t1, t2, t3, t4, t5) _.p(s) def plog(_, gamename, gamenumber, date, status, gameid = -1, won = -1): _.p('%-25s %-20s %17s %s\n' % (gamename, gamenumber, date, status)) class FileWriter(StringWriter): def __init__(_, file): _.file = file def p(_, s): _.file.write(s) def writeHeader(_, writer, header, pagewidth = 72): date = time.ctime(time.time()) date = time.strftime('%Y-%m-%d %H:%M', time.localtime(time.time())) blanks = max(pagewidth - len(header) - len(date), 1) writer.pheader(header + ' ' * blanks + date + '\n') writer.pheader('-' * pagewidth + '\n') writer.pheader('\n') def writeStats(_, writer, player, header): app = _.app _.writeHeader(writer, header, 62) if not player: pass writer.pstats('Demo games', 'Played', 'Won', 'Lost', '% won') writer.nl() (twon, tlost, tgames) = (0, 0, 0) g = app.getGamesIdSortedByName() for id in g: name = app.getGameMenuitemName(id) (won, lost) = app.stats.getStats(player, id) (twon, tlost) = (twon + won, tlost + lost) if won > 0 and lost > 0 or id == app.game.id: writer.pstats(name, won + lost, won, lost, perc, gameid = id) tgames = tgames + 1 writer.nl() (won, lost) = (twon, tlost) if won + lost > 0: perc = '%.1f' % 100.0 * won / (won + lost) else: perc = '0.0' writer.pstats('Total (%d out of %d games)' % (tgames, len(g)), won + lost, won, lost, perc) writer.nl(2) return tgames def _writeLog(_, writer, player, header, prev_games): if not player or not prev_games: return 0 _.writeHeader(writer, header, 71) writer.plog('Game', 'Game number', 'Started at ', 'Status') writer.nl() (twon, tlost) = (0, 0) for pg in prev_games: if len(pg) == 5: pg = pg + ('', None, None, 1) elif len(pg) == 7: pg = pg + (None, 1) elif len(pg) == 8: pg = pg + (1,) if len(pg) < 8: continue gameid = pg[0] if type(gameid) is not types.IntType: continue gi = _.app.getGameInfo(gameid) if not gi: gi = _.app.getGameInfo(GI.PROTECTED_GAMES.get(gameid)) if gi: name = gi.short_name else: name = '** UNKNOWN %d **' % gameid f = pg[1] if len(f) == 16: gamenumber = '%s-%s-%s' % (f[4:8], f[8:12], f[12:16]) elif len(f) <= 20: gamenumber = f else: gamenumber = '** ERROR **' date = time.strftime('%Y-%m-%d %H:%M', time.localtime(pg[3])) if pg[2] >= 0: won = pg[2] > 0 (twon, tlost) = (twon + won, tlost + (1 - won)) status = '*error*' if pg[2] <= pg[2]: pass elif pg[2] <= 2: status = ('Loaded', 'Not won', 'Lost', 'Won', 'Perfect')[pg[2] + 2] writer.plog(name, gamenumber, date, status, gameid = gameid, won = pg[2]) writer.nl(2) return 1 def writeFullLog(_, writer, player, header): prev_games = _.app.stats.prev_games.get(player) return _._writeLog(writer, player, header, prev_games) def writeSessionLog(_, writer, player, header): prev_games = _.app.stats.session_games.get(player) return _._writeLog(writer, player, header, prev_games) class PysolMenubarActions: def __init__(_, app, top): _.app = app _.top = top _.game = None _.menustate = Struct(save = 0, save_as = 0, hold_and_quit = 0, undo = 0, redo = 0, restart = 0, deal = 0, hint = 0, autofaceup = 0, autodrop = 0, autodeal = 0, quickplay = 0, demo = 0, highlight_piles = 0, rules = 0) _.tkopt = Struct(gameid = MfxRadioMenuItem(_), gameid_popular = MfxRadioMenuItem(_), comment = MfxCheckMenuItem(_), autofaceup = MfxCheckMenuItem(_), autodrop = MfxCheckMenuItem(_), autodeal = MfxCheckMenuItem(_), quickplay = MfxCheckMenuItem(_), undo = MfxCheckMenuItem(_), bookmarks = MfxCheckMenuItem(_), hint = MfxCheckMenuItem(_), highlight_piles = MfxCheckMenuItem(_), highlight_cards = MfxCheckMenuItem(_), highlight_samerank = MfxCheckMenuItem(_), sound = MfxCheckMenuItem(_), cardback = MfxRadioMenuItem(_), tabletile = MfxRadioMenuItem(_), animations = MfxRadioMenuItem(_), shadow = MfxCheckMenuItem(_), shade = MfxCheckMenuItem(_), toolbar = MfxRadioMenuItem(_), toolbar_size = MfxRadioMenuItem(_), toolbar_relief = MfxRadioMenuItem(_), statusbar = MfxCheckMenuItem(_)) def connectGame(_, game): _.game = game if game is None: return None if not __debug__ and _.app is game.app: raise AssertionError (tkopt, opt) = (_.tkopt, _.app.opt) tkopt.gameid.set(game.id) tkopt.gameid_popular.set(game.id) tkopt.comment.set(bool(game.gsaveinfo.comment)) tkopt.autofaceup.set(opt.autofaceup) tkopt.autodrop.set(opt.autodrop) tkopt.autodeal.set(opt.autodeal) tkopt.quickplay.set(opt.quickplay) tkopt.undo.set(opt.undo) tkopt.hint.set(opt.hint) tkopt.bookmarks.set(opt.bookmarks) tkopt.highlight_piles.set(opt.highlight_piles) tkopt.highlight_cards.set(opt.highlight_cards) tkopt.highlight_samerank.set(opt.highlight_samerank) tkopt.sound.set(opt.sound) tkopt.cardback.set(_.app.cardset.backindex) (tkopt.tabletile.set(_.app.tabletile_index),) tkopt.animations.set(opt.animations) tkopt.shadow.set(opt.shadow) tkopt.shade.set(opt.shade) tkopt.toolbar.set(opt.toolbar) tkopt.toolbar_size.set(opt.toolbar_size) tkopt.toolbar_relief.set(opt.toolbar_relief) tkopt.statusbar.set(opt.statusbar) def updateRecentGamesMenu(_, gameids): pass def updateBookmarkMenuState(_): pass def updateBackgroundImagesMenu(_): pass def _finishDrag(_): if not _.game is None: pass return _.game._finishDrag() def _cancelDrag(_): if not _.game is None: pass return _.game._cancelDrag() def changed(_, *args, **kw): if not __debug__ and _.game is not None: raise AssertionError return apply(_.game.changed, args, kw) def setMenuState(_, state, path): raise SubclassResponsibility def setToolbarState(_, state, path): raise SubclassResponsibility def _clearMenuState(_): ms = _.menustate for k, v in ms.__dict__.items(): pass def _updateMenuState(_): _._clearMenuState() game = _.game if not __debug__ and game is not None: raise AssertionError opt = _.app.opt ms = _.menustate ms.save_as = game.canSaveGame() ms.hold_and_quit = ms.save_as if game.filename and ms.save_as: ms.save = 1 if opt.undo: if game.canUndo() and game.moves.index > 0: ms.undo = 1 if game.canRedo() and game.moves.index < len(game.moves.history): ms.redo = 1 if game.moves.index > 0: ms.restart = 1 if game.canDealCards(): ms.deal = 1 if game.getHintClass() is not None: if opt.hint: ms.hint = 1 ms.demo = 1 autostacks = game.getAutoStacks() if autostacks[0]: ms.autofaceup = 1 if autostacks[1] and game.s.foundations: ms.autodrop = 1 if game.s.waste: ms.autodeal = 1 if autostacks[2]: ms.quickplay = 1 ms.highlight_piles = 0 if opt.highlight_piles and game.getHighlightPilesStacks(): ms.highlight_piles = 1 if game.app.getGameRulesFilename(game.id): ms.rules = 1 def _updateMenus(_): if _.game is None: return None ms = _.menustate _.setMenuState(ms.save, 'file.save') _.setMenuState(ms.save_as, 'file.saveas') _.setMenuState(ms.hold_and_quit, 'file.holdandquit') _.setMenuState(ms.undo, 'edit.undo') _.setMenuState(ms.redo, 'edit.redo') _.setMenuState(ms.redo, 'edit.redoall') _.updateBookmarkMenuState() _.setMenuState(ms.restart, 'edit.restartgame') _.setMenuState(ms.deal, 'game.dealcards') _.setMenuState(ms.autodrop, 'game.autodrop') _.setMenuState(ms.hint, 'assist.hint') _.setMenuState(ms.highlight_piles, 'assist.highlightpiles') _.setMenuState(ms.demo, 'assist.demo') _.setMenuState(ms.demo, 'assist.demoallgames') _.setMenuState(ms.autofaceup, 'options.automaticplay.autofaceup') _.setMenuState(ms.autodrop, 'options.automaticplay.autodrop') _.setMenuState(ms.autodeal, 'options.automaticplay.autodeal') _.setMenuState(ms.quickplay, 'options.automaticplay.quickplay') _.setMenuState(ms.rules, 'help.rulesforthisgame') _.setToolbarState(ms.restart, 'restart') _.setToolbarState(ms.save_as, 'save') _.setToolbarState(ms.undo, 'undo') _.setToolbarState(ms.redo, 'redo') _.setToolbarState(ms.autodrop, 'autodrop') _.setToolbarState(ms.rules, 'rules') _.tkopt.comment.set(bool(_.game.gsaveinfo.comment)) def updateMenus(_): if _.game is None: return None _._updateMenuState() _._updateMenus() def disableMenus(_): if _.game is None: return None _._clearMenuState() _._updateMenus() def mNewGame(_, *args): if _._cancelDrag(): return None if _.changed(): if not _.game.areYouSure('New game'): return None if _.game.nextGameFlags(_.game.id) == 0: _.game.endGame() _.game.newGame() else: _.game.endGame() _.game.quitGame(_.game.id) def _mSelectGame(_, id, random = None): if _._cancelDrag(): return None if _.game.id == id: return None if _.changed(): if not _.game.areYouSure('Select game'): _.tkopt.gameid.set(_.game.id) _.tkopt.gameid_popular.set(_.game.id) return None _.game.endGame() _.game.quitGame(id, random = random) def mSelectGame(_, *args): _._mSelectGame(_.tkopt.gameid.get()) def mSelectGamePopular(_, *args): _._mSelectGame(_.tkopt.gameid_popular.get()) def _mNewGameBySeed(_, seed, origin): try: (id, random) = constructRandom(seed) if id is None: id = _.game.id if random is None: return None if not _.app.getGameInfo(id): raise ValueError except (ValueError, TypeError): ex = None d = MfxDialog(_.top, title = 'Invalid game number', text = 'Invalid game number\n' + str(seed), bitmap = 'error') return None f = _.game.nextGameFlags(id, random) if f & 17 == 0: return None random.origin = origin if f & 15 == 0: _.game.endGame() _.game.newGame(random = random) else: _.game.endGame() _.game.quitGame(id, random = random) def mNewGameWithNextId(_, *args): if _.changed(): if not _.game.areYouSure('Select next game number'): return None r = _.game.random seed = r.increaseSeed(r.initial_seed) seed = r.str(seed) _._mNewGameBySeed(seed, _.game.random.ORIGIN_NEXT_GAME) def mSelectGameById(_, *args): if _._cancelDrag(): return None (id, f) = (None, _.game.getGameNumber(format = 0)) d = MfxSimpleEntry(_.top, 'Select new game number', '\n\nEnter new game number', f, strings = ('OK', 'Next number', 'Cancel'), default = 0, e_width = 25) if d.status != 0: return None if d.button == 2: return None if d.button == 1: _.mNewGameWithNextId() return None if _.changed(): if not _.game.areYouSure('Select new game number'): return None _._mNewGameBySeed(d.value, _.game.random.ORIGIN_SELECTED) def mSelectRandomGame(_, *args): if _._cancelDrag(): return None if _.changed(): if not _.game.areYouSure('Select random game'): return None for i in range(1000): gi = _.app.getGameInfo(_.app.getRandomGameId()) if 1 and gi.id == _.game.id: continue if 1 and gi.category != _.game.gameinfo.category: continue if gi and gi.id != _.game.id: _.game.endGame() _.game.quitGame(gi.id) def _mSelectNextGameFromList(_, gl, step): if _._cancelDrag(): return None id = _.game.id gl = list(gl) if len(gl) < 2 or not (id in gl): return None if _.changed(): if not _.game.areYouSure('Select next game'): return None index = (gl.index(id) + step) % len(gl) _.game.endGame() _.game.quitGame(gl[index]) def mSelectNextGameById(_, *args): _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedById(), 1) def mSelectPrevGameById(_, *args): _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedById(), -1) def mSelectNextGameByName(_, *args): _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedByName(), 1) def mSelectPrevGameByName(_, *args): _._mSelectNextGameFromList(_.app.gdb.getGamesIdSortedByName(), -1) def mSave(_, *args): if _._cancelDrag(): return None if _.menustate.save_as: if _.game.filename: _.game.saveGame(_.game.filename) else: _.mSaveAs() def mHoldAndQuit(_, *args): if _._cancelDrag(): return None _.game.endGame(holdgame = 1) _.game.quitGame(holdgame = 1) def mQuit(_, *args): if _._cancelDrag(): return None if _.changed(): if not _.game.areYouSure('Quit PySol'): return None _.game.endGame() _.game.quitGame() def mUndo(_, *args): if _._cancelDrag(): return None if _.menustate.undo: _.game.playSample('undo') _.game.undo() def mRedo(_, *args): if _._cancelDrag(): return None if _.menustate.redo: _.game.playSample('redo') _.game.redo() _.game.checkForWin() def mRedoAll(_, *args): if _._cancelDrag(): return None if _.menustate.redo: _.game.playSample('redo', loop = 1) while _.game.moves.index < len(_.game.moves.history): _.game.redo() if _.game.checkForWin(): break _.game.stopSamples() def mSetBookmark(_, n, confirm = 1): if _._cancelDrag(): return None if not (_.app.opt.bookmarks): return None if not None if n <= n else n <= 8: return None _.game.setBookmark(n, confirm = confirm) _.game.updateMenus() def mGotoBookmark(_, n, confirm = -1): if _._cancelDrag(): return None if not (_.app.opt.bookmarks): return None if not None if n <= n else n <= 8: return None _.game.gotoBookmark(n, confirm = confirm) _.game.updateMenus() def mClearBookmarks(_, *args): if _._cancelDrag(): return None if not (_.app.opt.bookmarks): return None if not (_.game.gsaveinfo.bookmarks): return None if not _.game.areYouSure('Clear bookmarks', 'Clear all bookmarks ?'): return None _.game.gsaveinfo.bookmarks = { } _.game.updateMenus() def mRestart(_, *args): if _._cancelDrag(): return None if _.game.moves.index == 0: return None if _.changed(restart = 1): if not _.game.areYouSure('Restart game', 'Restart this game ?'): return None _.game.restartGame() def mDeal(_, *args): if _._cancelDrag(): return None _.game.dealCards() def mDrop(_, *args): if _._cancelDrag(): return None _.game.autoPlay(autofaceup = -1, autodrop = 1) def mDrop1(_, *args): if _._cancelDrag(): return None _.game.autoPlay(autofaceup = 1, autodrop = 1) def mStatus(_, *args): if _._cancelDrag(): return None _.mPlayerStats(mode = 100) def mEditGameComment(_, *args): if _._cancelDrag(): return None (game, gi) = (_.game, _.game.gameinfo) t = ' ' + game.getGameNumber(format = 1) cc = 'Comments for ' + gi.name + t + ':\n\n' if not game.gsaveinfo.comment: pass c = cc d = EditTextDialog(game.top, 'Comments for' + t, text = c) if d.status == 0 and d.button == 0: if string.strip(d.text) == string.strip(cc): game.gsaveinfo.comment = '' else: game.gsaveinfo.comment = string.rstrip(d.text) _.tkopt.comment.set(bool(game.gsaveinfo.comment)) def _mStatsSave(_, player, header, filename, write_method): file = None if player is None: text = 'Demo statistics' filename = filename + '_demo' else: text = 'Your statistics' filename = os.path.join(_.app.dn.config, filename + '.txt') filename = os.path.normpath(filename) try: file = open(filename, 'a') a = PysolStatsFormatter(_.app) writer = a.FileWriter(file) apply(write_method, (a, writer, player, header)) destruct(a) except EnvError: ex = None if file: file.close() d = MfxExceptionDialog(_.top, ex, text = 'Error while writing to file') if file: file.close() d = MfxDialog(_.top, title = PACKAGE + ' Info', bitmap = 'info', text = text + ' were appended to\n\n' + filename) def mPlayerStats(_, *args, **kw): mode = kw.get('mode', 101) demo = 0 while mode > 0: if mode > 1000: demo = not demo mode = mode % 1000 d = Struct(status = -1, button = -1) if demo: player = None (p0, p1, p2) = (PACKAGE + ' Demo', PACKAGE + ' Demo ', '') else: player = _.app.opt.player (p0, p1, p2) = (player, '', ' for ' + player) n = _.game.gameinfo.short_name if mode == 100: d = Status_StatsDialog(_.top, game = _.game) elif mode == 101: header = p1 + 'Statistics for ' + n d = SingleGame_StatsDialog(_.top, header, _.app, player, gameid = _.game.id) elif mode == 102: header = p1 + 'Statistics' + p2 d = AllGames_StatsDialog(_.top, header, _.app, player) elif mode == 103: header = p1 + 'Full log' + p2 d = FullLog_StatsDialog(_.top, header, _.app, player) elif mode == 104: header = p1 + 'Session log' + p2 d = SessionLog_StatsDialog(_.top, header, _.app, player) elif mode == 202: header = 'Statistics for ' + p0 write_method = PysolStatsFormatter.writeStats _._mStatsSave(player, header, 'stats', write_method) elif mode == 203: header = 'Full log for ' + p0 write_method = PysolStatsFormatter.writeFullLog _._mStatsSave(player, header, 'log', write_method) elif mode == 204: header = 'Session log for ' + p0 write_method = PysolStatsFormatter.writeSessionLog _._mStatsSave(player, header, 'log', write_method) elif mode == 301: if _.game.areYouSure('Reset all statistics', 'Reset ALL statistics and logs for player\n' + p0 + ' ?', confirm = 1, default = 1): _.app.stats.resetStats(player, 0) _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id)) elif mode == 302: if _.game.areYouSure('Reset game statistics', 'Reset statistics and logs for player\n' + p0 + '\nand game\n' + n + ' ?', confirm = 1, default = 1): _.app.stats.resetStats(player, _.game.id) _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id)) elif mode == 401: pass elif mode == 402: pass else: print 'stats problem:', mode, demo, player if d.status != 0: break mode = d.button def mHint(_, *args): if _._cancelDrag(): return None if _.app.opt.hint: if _.game.showHint(0, _.app.opt.hint_sleep): _.game.stats.hints = _.game.stats.hints + 1 def mHint1(_, *args): if _._cancelDrag(): return None if _.app.opt.hint: if _.game.showHint(1, _.app.opt.hint_sleep): _.game.stats.hints = _.game.stats.hints + 1 def mHighlightPiles(_, *args): if _._cancelDrag(): return None if _.app.opt.highlight_piles: if _.game.highlightPiles(_.app.opt.highlight_piles_sleep): _.game.stats.highlight_piles = _.game.stats.highlight_piles + 1 def mDemo(_, *args): if _._cancelDrag(): return None if _.game.getHintClass() is not None: _._mDemo(mixed = 0) def mMixedDemo(_, *args): if _._cancelDrag(): return None _._mDemo(mixed = 1) def _mDemo(_, mixed): if _._cancelDrag(): return None if _.changed(): if _.game.stats.demo_moves == 0 and _.game.stats.hints == 0: if not _.game.areYouSure('Play demo'): return None _.game.startDemo(mixed = mixed) def mOptPlayerOptions(_, *args): if _._cancelDrag(): return None d = PlayerOptionsDialog(_.top, 'Set player options', _.app) if d.status == 0 and d.button == 0: _.app.opt.confirm = bool(d.confirm) _.app.opt.update_player_stats = bool(d.update_stats) _.app.opt.win_animation = bool(d.win_animation) n = string.strip(d.player[:30]) if len(n) < len(n): pass elif len(n) <= 30: _.app.opt.player = n _.game.updateStatus(player = _.app.opt.player) _.game.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.game.id)) def mOptAutoFaceUp(_, *args): if _._cancelDrag(): return None _.app.opt.autofaceup = _.tkopt.autofaceup.get() if _.app.opt.autofaceup: _.game.autoPlay() def mOptAutoDrop(_, *args): if _._cancelDrag(): return None _.app.opt.autodrop = _.tkopt.autodrop.get() if _.app.opt.autodrop: _.game.autoPlay() def mOptAutoDeal(_, *args): if _._cancelDrag(): return None _.app.opt.autodeal = _.tkopt.autodeal.get() if _.app.opt.autodeal: _.game.autoPlay() def mOptQuickPlay(_, *args): if _._cancelDrag(): return None _.app.opt.quickplay = _.tkopt.quickplay.get() def mOptEnableUndo(_, *args): if _._cancelDrag(): return None _.app.opt.undo = _.tkopt.undo.get() _.game.updateMenus() def mOptEnableBookmarks(_, *args): if _._cancelDrag(): return None _.app.opt.bookmarks = _.tkopt.bookmarks.get() _.game.updateMenus() def mOptEnableHint(_, *args): if _._cancelDrag(): return None _.app.opt.hint = _.tkopt.hint.get() _.game.updateMenus() def mOptEnableHighlightPiles(_, *args): if _._cancelDrag(): return None _.app.opt.highlight_piles = _.tkopt.highlight_piles.get() _.game.updateMenus() def mOptEnableHighlightCards(_, *args): if _._cancelDrag(): return None _.app.opt.highlight_cards = _.tkopt.highlight_cards.get() _.game.updateMenus() def mOptEnableHighlightSameRank(_, *args): if _._cancelDrag(): return None _.app.opt.highlight_samerank = _.tkopt.highlight_samerank.get() _.game.updateMenus() def mOptSound(_, *args): if _._cancelDrag(): return None _.app.opt.sound = _.tkopt.sound.get() if not (_.app.opt.sound): _.app.audio.stopAll() def mOptSoundDialog(_, *args): if _._cancelDrag(): return None d = SoundOptionsDialog(_.top, 'Sound settings', _.app) _.tkopt.sound.set(_.app.opt.sound) def mOptAnimations(_, *args): if _._cancelDrag(): return None _.app.opt.animations = _.tkopt.animations.get() def mOptShadow(_, *args): if _._cancelDrag(): return None _.app.opt.shadow = _.tkopt.shadow.get() def mOptShade(_, *args): if _._cancelDrag(): return None _.app.opt.shade = _.tkopt.shade.get() def mOptIrregularPiles(_, *args): if _._cancelDrag(): return None _.app.opt.irregular_piles = _.tkopt.irregular_piles.get() def mOptDemoOptions(_, *args): if _._cancelDrag(): return None d = DemoOptionsDialog(_.top, 'Set demo options', _.app) if d.status == 0 and d.button == 0: _.app.opt.demo_logo = d.demo_logo _.app.opt.demo_score = d.demo_score _.app.opt.demo_sleep = d.demo_sleep def mOptHintOptions(_, *args): if _._cancelDrag(): return None d = HintOptionsDialog(_.top, 'Set hint options', _.app) if d.status == 0 and d.button == 0: _.app.opt.hint_sleep = d.hint_sleep def mOptSave(_, *args): if _._cancelDrag(): return None try: _.app.saveOptions() except Exception: ex = None d = MfxExceptionDialog(_.top, ex, text = 'Error while saving options') d = MfxDialog(_.top, title = PACKAGE + ' Info', text = 'Options were saved to\n\n' + _.app.fn.opt, bitmap = 'info') def mHelp(_, *args): if _._cancelDrag(): return None helpHTML(_.app, 'index.html', 'html') def mHelpHowToPlay(_, *args): if _._cancelDrag(): return None helpHTML(_.app, 'howtoplay.html', 'html') def mHelpRules(_, *args): if _._cancelDrag(): return None if not (_.menustate.rules): return None dir = os.path.join('html', 'rules') helpHTML(_.app, _.app.getGameRulesFilename(_.game.id), dir) def mHelpLicense(_, *args): if _._cancelDrag(): return None helpHTML(_.app, 'license.html', 'html') def mHelpNews(_, *args): if _._cancelDrag(): return None helpHTML(_.app, 'news.html', 'html') def mHelpWebSite(_, *args): openURL(PACKAGE_URL) def mHelpAbout(_, *args): if _._cancelDrag(): return None if _.app and bundle & 4: helpAboutSimple(_.app) else: helpAbout(_.app) def mScreenshot(_, *args): if _._cancelDrag(): return None f = os.path.join(_.app.dn.config, 'screenshots') if not os.path.isdir(f): return None f = os.path.join(f, _.app.getGameSaveName(_.game.id)) i = 1 while 1: fn = f + '-%d.ppm' % i if not os.path.isfile(fn): break i = i + 1 if i >= 10000: return None _.top.screenshot(fn) def mPlayNextMusic(_, *args): if _._cancelDrag(): return None if _.app.audio and _.app.opt.sound_music_volume > 0: _.app.audio.playNextMusic() if 1 and _.app.debug: index = _.app.audio.getMusicInfo() music = _.app.music_manager.get(index) if music: print 'playing music:', music.filename class PysolToolbarActions: def __init__(_): _.game = None _.menubar = None def connectGame(_, game, menubar): _.game = game _.menubar = menubar def _busy(_): raise SubclassResponsibility def mNewGame(_, *args): if not _._busy(): _.menubar.mNewGame() return 1 def mOpen(_, *args): if not _._busy(): _.menubar.mOpen() return 1 def mRestart(_, *args): if not _._busy(): _.menubar.mRestart() return 1 def mSave(_, *args): if not _._busy(): _.menubar.mSaveAs() return 1 def mUndo(_, *args): if not _._busy(): _.menubar.mUndo() return 1 def mRedo(_, *args): if not _._busy(): _.menubar.mRedo() return 1 def mDrop(_, *args): if not _._busy(): _.menubar.mDrop() return 1 def mStatus(_, *args): if not _._busy(): _.menubar.mStatus() return 1 def mPlayerStats(_, *args): if not _._busy(): _.menubar.mPlayerStats() return 1 def mHelpRules(_, *args): if not _._busy(): _.menubar.mHelpRules() return 1 def mQuit(_, *args): if not _._busy(): _.menubar.mQuit() return 1 def mOptPlayerOptions(_, *args): if not _._busy(): _.menubar.mOptPlayerOptions() return 1 class _MfxToolbar: def __init__(_, top, relief = 0): _.top = top _.button_bg = None _._MfxToolbar__setRelief(relief) _.side = -1 _._tooltips = [] _._widgets = [] _._icon_height = 0 _.canvas = Tkinter.Canvas(_.top, bd = 0, highlightthickness = 0) _.frame = Tkinter.Frame(_.canvas, bd = 0, highlightthickness = 1) _._initFrame() _.canvas.pack(side = Tkinter.TOP, fill = Tkinter.X) def _MfxToolbar__setRelief(_, relief): if type(relief) is types.IntType: relief = (Tkinter.RAISED, Tkinter.FLAT)[relief] elif relief in (Tkinter.RAISED, Tkinter.FLAT): pass else: relief = Tkinter.FLAT _.button_relief = relief if relief == Tkinter.RAISED: _.separator_relief = Tkinter.FLAT else: _.separator_relief = Tkinter.RAISED return relief def _initFrame(_): _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X) def _createSeparator(_, width = 12, side = Tkinter.LEFT, relief = None): if relief is None: relief = _.separator_relief if relief == Tkinter.FLAT or width <= 6: sep = Tkinter.Frame(_.frame, highlightthickness = 0, width = width, height = 0, takefocus = 0, relief = relief) padx = 0 elif not _._icon_height: pass height = max(38 - 4, 20) sep = Tkinter.Frame(_.frame, bd = 1, highlightthickness = 1, width = 4, height = height, takefocus = 0, relief = relief) padx = (width - 4) / 2 sep.pack(side = side, padx = padx) _._widgets.append(sep) def show(_, side = 1, resize = 1): if _.side == side: return 0 if resize: _.top.wm_geometry('') if not side: if 0 and TK_DASH_PATCH: _.canvas.config(state = 'hidden') else: _.canvas.pack_propagate(0) _.canvas.config(height = 0) elif 0 and TK_DASH_PATCH: _.canvas.config(state = 'normal') else: _.canvas.pack_propagate(1) s = (None, Tkinter.TOP, Tkinter.BOTTOM)[side] _.canvas.pack(side = s, fill = Tkinter.X) _.side = side return 1 def hide(_, resize = 1): _.show(0, resize) def getSide(_): return _.side def destroy(_): for w in _._tooltips: pass _._tooltips = [] for w in _._widgets: pass _._widgets = [] def setCursor(_, cursor): if _.side: _.frame.config(cursor = cursor) _.frame.update_idletasks() class PysolToolbar(_MfxToolbar, PysolToolbarActions): def __init__(_, top, dir, size, relief = 0): _MfxToolbar.__init__(_, top, relief) PysolToolbarActions.__init__(_) _.dir = dir _.size = size _._createButton('new', _.mNewGame, tooltip = 'New game') _._createButton('open', _.mOpen, tooltip = 'Open a \nsaved game') _._createSeparator() _._createButton('restart', _.mRestart, tooltip = 'Restart the \ncurrent game') _._createButton('save', _.mSave, tooltip = 'Save game') _._createSeparator() _._createButton('undo', _.mUndo, tooltip = 'Undo last move') _._createButton('redo', _.mRedo, tooltip = 'Redo last move') _._createButton('autodrop', _.mDrop, tooltip = 'Auto drop cards') _._createSeparator() _._createButton('stats', _.mPlayerStats, tooltip = 'View statistics') _._createButton('rules', _.mHelpRules, tooltip = 'Rules for this game') _._createSeparator() _._createButton('quit', _.mQuit, tooltip = 'Quit PySol') _._createSeparator(width = 20, relief = Tkinter.FLAT) _._createLabel('player', padx = 8, tooltip = 'Change name of\ncurrent player') _.player_label.bind('<1>', _.mOptPlayerOptions) _.popup = None _.frame.bind('<1>', _.clickHandler) _.frame.bind('<3>', _.rightclickHandler) def _initFrame(_): if 0 or os.name == 'nt': _.frame.config(bd = 1, relief = Tkinter.SOLID) _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, ipady = 4) _._createSeparator(width = 4, side = Tkinter.LEFT, relief = Tkinter.FLAT) _._createSeparator(width = 4, side = Tkinter.RIGHT, relief = Tkinter.FLAT) else: _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, ipady = 1) def _loadImage(_, name): file = os.path.join(_.dir, name) image = None for ext in IMAGE_EXTENSIONS: file = os.path.join(_.dir, name + ext) return image def _createButton(_, name, command, padx = 0, tooltip = None): image = _._loadImage(name) button = Tkinter.Button(_.frame, command = command, takefocus = 0, relief = _.button_relief) button.toolbar_name = name if image: button.config(image = image) if _._icon_height == 0: _._icon_height = image.height() if _.button_relief == Tkinter.FLAT: if _.button_bg is None: _.button_bg = _._getButtonBg(button['activebackground']) button['activebackground'] = _.button_bg button.pack(side = Tkinter.LEFT, padx = padx) setattr(_, name + '_image', image) setattr(_, name + '_button', button) _._widgets.append(button) if tooltip: b = MfxTooltip(button) _._tooltips.append(b) b.setText(tooltip) def _getButtonBg(_, col): if type(col) is not types.StringType and col[0] != '#' or len(col) != 7: return '#f0f0f0' c = '#' for i in (1, 3, 5): v = string.atoi(col[i:i + 2], 16) v = int(v + 24) c = c + '%02x' % v return c def _createLabel(_, name, padx = 0, side = Tkinter.RIGHT, tooltip = None): aspect = (400, 300)[_.size != 0] label = Tkinter.Message(_.frame, relief = 'ridge', justify = 'center', aspect = aspect) label.pack(side = side, padx = padx) setattr(_, name + '_label', label) _._widgets.append(label) if tooltip: b = MfxTooltip(label) _._tooltips.append(b) b.setText(tooltip) def _busy(_): if not (_.side) and not (_.game) or not (_.menubar): return 1 if _.game.demo: _.game.stopDemo() return _.game.busy def connectGame(_, game, menubar): PysolToolbarActions.connectGame(_, game, menubar) if _.popup: _.popup.destroy() destruct(_.popup) _.popup = None if menubar: tkopt = menubar.tkopt _.popup = Tkinter.Menu(_.canvas, tearoff = 0) _.popup.add_command(label = 'Toolbar', state = Tkinter.DISABLED) _.popup.add_radiobutton(label = 'Hide', variable = tkopt.toolbar, value = 0, command = menubar.mOptToolbar, underline = 0) _.popup.add_radiobutton(label = 'Top', variable = tkopt.toolbar, value = 1, command = menubar.mOptToolbar, underline = 0) _.popup.add_radiobutton(label = 'Bottom', variable = tkopt.toolbar, value = 2, command = menubar.mOptToolbar, underline = 0) if 1: _.popup.add_separator() _.popup.add_radiobutton(label = 'Small icons', variable = tkopt.toolbar_size, value = 0, command = menubar.mOptToolbarSize, underline = 0) _.popup.add_radiobutton(label = 'Large icons', variable = tkopt.toolbar_size, value = 1, command = menubar.mOptToolbarSize, underline = 0) def setRelief(_, relief): pass def updateText(_, **kw): for name in kw.keys(): label = getattr(_, name + '_label') label['text'] = str(kw[name]) def updateImages(_, dir, size): if dir == _.dir and size == _.size: return 0 if not os.path.isdir(dir): return 0 (old_dir, old_size) = (_.dir, _.size) (_.dir, _.size) = (dir, size) data = [] try: l = _.player_label for w in _._widgets: name = w.toolbar_name image = _._loadImage(name) data.append((name, w, image)) except: (_.dir, _.size) = (old_dir, old_size) return 0 aspect = (400, 300)[size != 0] l.config(aspect = aspect) for name, w, image in data: w.config(image = image) setattr(_, name + '_image', image) return 1 def clickHandler(_, event): if _._busy(): return EVENT_HANDLED return EVENT_HANDLED def rightclickHandler(_, event): if _._busy(): return EVENT_HANDLED if _.popup: _.popup.tk_popup(event.x_root, event.y_root) return EVENT_HANDLED def middleclickHandler(_, event): if _._busy(): return EVENT_HANDLED if _.side <= _.side: pass elif _.side <= 2: _.menubar.setToolbarSide(3 - _.side) return EVENT_HANDLED class _MfxStatusbar: def __init__(_, top): _.top = top _.side = '#init#' _._widgets = [] _._tooltips = [] _.padx = 0 _.pady = 0 _.canvas = Tkinter.Canvas(_.top, bd = 0, highlightthickness = 0) _.frame = Tkinter.Frame(_.canvas, bd = 1, relief = Tkinter.RAISED) _.frame.pack(side = Tkinter.TOP, fill = Tkinter.X, pady = _.pady) _.canvas.pack(side = Tkinter.BOTTOM, fill = Tkinter.X) def _createLabel(_, name, text = '', relief = Tkinter.SUNKEN, side = Tkinter.LEFT, fill = Tkinter.NONE, padx = -1, expand = 0, tooltip = None): if padx < 0: padx = _.padx label = Tkinter.Label(_.frame, text = text, relief = relief, fg = '#000000') label.pack(side = side, fill = fill, padx = padx, expand = expand) setattr(_, name + '_label', label) _._widgets.append(label) if tooltip: b = MfxTooltip(label) _._tooltips.append(b) b.setText(tooltip) return label def updateText(_, **kw): for k, v in kw.items(): label = getattr(_, k + '_label') label['text'] = str(v) def configLabel(_, name, **kw): label = getattr(_, name + '_label') apply(label.config, (), kw) def show(_, side = Tkinter.BOTTOM, resize = 0): if side in (0, '', 'none', 'None'): side = None elif not (side in (None, Tkinter.BOTTOM)): side = Tkinter.BOTTOM if _.side == side: return 0 if resize: _.top.wm_geometry('') if not side: _.canvas.pack_propagate(0) _.canvas.config(height = 0) else: _.canvas.pack_propagate(1) _.canvas.pack(side = side, fill = Tkinter.X) _.side = side return 1 def hide(_, resize = 0): _.show(None, resize) def getSide(_): return _.side def destroy(_): for w in _._tooltips: pass _._tooltips = [] for w in _._widgets: pass _._widgets = [] class PysolStatusbar(_MfxStatusbar): def __init__(_, top): _MfxStatusbar.__init__(_, top) _._createLabel('moves', tooltip = 'Number of moves \nin this game') _._createLabel('gamenumber', tooltip = 'Game number') _._createLabel('stats', tooltip = 'Games played: won/lost') l = _._createLabel('info', fill = Tkinter.X, expand = 0) l.config(text = '', justify = 'left') _._widgets[0].grid_configure(column = 0, row = 0, sticky = 'ew') _._widgets[1].grid_configure(column = 1, row = 0, sticky = 'ew') _._widgets[2].grid_configure(column = 2, row = 0, sticky = 'ew') _._widgets[3].grid_configure(column = 3, row = 0, sticky = 'ew') _.frame.grid_columnconfigure(0, weight = 0, minsize = 100) _.frame.grid_columnconfigure(1, weight = 0, minsize = 200) _.frame.grid_columnconfigure(2, weight = 0, minsize = 100) _.frame.grid_columnconfigure(3, weight = 1) class PysolProgressBar: def __init__(_, app, parent, title = None, images = None, color = 'blue', bg = '#c0c0c0', width = 300, height = 25, show_text = 1): _.parent = parent _.percent = 0 _.top = makeToplevel(parent, title = title) _.top.wm_protocol('WM_DELETE_WINDOW', _.wmDeleteWindow) _.top.wm_group(parent) _.top.wm_resizable(0, 0) _.frame = Tkinter.Frame(_.top, relief = Tkinter.FLAT, bd = 0, bg = bg, takefocus = 0) _.cframe = Tkinter.Frame(_.frame, relief = Tkinter.SUNKEN, bd = 1, bg = bg, takefocus = 0) _.canvas = Tkinter.Canvas(_.cframe, width = width, height = height, bg = bg, takefocus = 0, bd = 0, highlightthickness = 0) _.scale = _.canvas.create_rectangle(-10, -10, 0, height, outline = color, fill = color) _.text = -1 if show_text: _.text = _.canvas.create_text(0, 0, anchor = Tkinter.CENTER) _.cframe.grid_configure(column = 0, row = 0, sticky = 'ew') if images: _.f1 = Tkinter.Label(_.frame, image = images[0], bg = bg) _.f1.grid_configure(column = 0, row = 0, sticky = 'ew', ipadx = 8, ipady = 4) _.cframe.grid_configure(column = 1, row = 0, sticky = 'ew', padx = 8) _.f2 = Tkinter.Label(_.frame, image = images[1], bg = bg) _.f2.grid_configure(column = 2, row = 0, sticky = 'ew', ipadx = 8, ipady = 4) _.top.config(cursor = 'watch') if app: try: wm_set_icon(_.top, app.dataloader.findIcon()) except: pass _.pack() if 1: setTransient(_.top, None, relx = 0.5, rely = 0.5) else: _.update(percent = 0) def wmDeleteWindow(_): return EVENT_HANDLED def destroy(_): if _.top is None: return None _.top.wm_withdraw() _.top.quit() _.top.destroy() _.top = None def pack(_, **kw): _.canvas.pack(fill = Tkinter.X, expand = 0) apply(_.frame.pack, (), kw) def reset(_, percent = 0): _.percent = percent def update(_, percent = None, step = 1): if _.top is None: return None if percent is None: _.percent = _.percent + step elif percent > _.percent: _.percent = percent else: return None _.percent = min(100, max(0, _.percent)) c = _.canvas (width, height) = (c.winfo_reqwidth(), c.winfo_reqheight()) c.coords(_.scale, -10, -10, _.percent * width / 100.0, height) if _.text >= 0: c.coords(_.text, width / 2, height / 2) c.itemconfig(_.text, text = '%d %%' % int(round(_.percent))) c.update() class MfxMenubar(Tkinter.Menu): addPath = None def __init__(_, master, **kw): if not __debug__ and kw.get('name'): raise AssertionError _.n = kw['tearoff'] = int(kw.get('tearoff', 0)) apply(Tkinter.Menu.__init__, (_, master), kw) def labeltoname(_, label): underline = -1 m = re.search('^(.*)\\&([^\\&].*)$', label) if m: (l1, l2) = (m.group(1), m.group(2)) l1 = re.sub('\\&\\&', '&', l1) l2 = re.sub('\\&\\&', '&', l2) label = l1 + l2 underline = len(l1) name = string.lower(re.sub('[^0-9a-zA-Z]', '', label)) return (name, label, underline) def add(_, itemType, cnf = { }): label = cnf.get('label') if label: (name, label, underline) = _.labeltoname(label) cnf['label'] = label cnf['underline'] = cnf.get('underline', underline) path = str(_._w) + '.' + name if _.addPath: _.addPath(path, _, _.n, cnf.get('menu')) Tkinter.Menu.add(_, itemType, cnf) _.n = _.n + 1 class MfxMenu(MfxMenubar): def __init__(_, master, label, underline = None, **kw): (name, label, label_underline) = _.labeltoname(label) kwdefault(kw, name = name) apply(MfxMenubar.__init__, (_, master), kw) if underline is None: underline = label_underline master.add_cascade(menu = _, label = label, underline = underline) class PysolMenubar(PysolMenubarActions): def __init__(_, app, top): PysolMenubarActions.__init__(_, app, top) sh = _.top.winfo_screenheight() _._PysolMenubar__cb_max = 25 if sh >= 600: _._PysolMenubar__cb_max = 30 if sh >= 768: _._PysolMenubar__cb_max = 35 _._PysolMenubar__menubar = None _._PysolMenubar__menupath = { } _._PysolMenubar__keybindings = { } _._createMenubar() _.updateBackgroundImagesMenu() _.top.config(menu = _._PysolMenubar__menubar) def _addPath(_, path, menu, index, submenu): if not _._PysolMenubar__menupath.has_key(path): _._PysolMenubar__menupath[path] = (menu, index, submenu) def _getEnabledState(_, enabled): if enabled: return 'normal' return 'disabled' def _createMenubar(_): MfxMenubar.addPath = _._addPath _._PysolMenubar__menubar = MfxMenubar(_.top, name = 'menubar') bind(_.top, '<KeyPress>', _._keyPressHandler) m = 'Ctrl-' if os.name == 'mac': m = 'Cmd-' menu = _._createMenu('&File') menu.add_command(label = '&New game', command = _.mNewGame, accelerator = 'N') submenu = MfxMenu(menu, label = 'R&ecent games') menu.add_command(label = 'Select &random game', command = _.mSelectRandomGame, accelerator = m + 'R') menu.add_command(label = 'Select game by nu&mber...', command = _.mSelectGameById, accelerator = m + 'M') menu.add_separator() menu.add_command(label = '&Open...', command = _.mOpen, accelerator = m + 'O') menu.add_command(label = '&Save', command = _.mSave, accelerator = m + 'S') menu.add_command(label = 'Save &as...', command = _.mSaveAs) menu.add_separator() menu.add_command(label = '&Hold and quit', command = _.mHoldAndQuit) menu.add_command(label = '&Quit', command = _.mQuit, accelerator = m + 'Q') menu = _._createMenu('&Select') _._addSelectGameMenu(menu) menu = _._createMenu('&Edit') menu.add_command(label = '&Undo', command = _.mUndo, accelerator = 'Z') menu.add_command(label = '&Redo', command = _.mRedo, accelerator = 'R') menu.add_command(label = 'Redo &all', command = _.mRedoAll) menu.add_separator() submenu = MfxMenu(menu, label = '&Set bookmark') for i in range(9): label = 'Bookmark %d' % (i + 1) submenu.add_command(label = label, command = (lambda _ = _, i = i: _.mSetBookmark(i))) submenu = MfxMenu(menu, label = 'Go&to bookmark') for i in range(9): label = 'Bookmark %d' % (i + 1) acc = m + '%d' % (i + 1) submenu.add_command(label = label, command = (lambda _ = _, i = i: _.mGotoBookmark(i)), accelerator = acc) menu.add_command(label = '&Clear bookmarks', command = _.mClearBookmarks) menu.add_separator() menu.add_command(label = 'Restart &game', command = _.mRestart, accelerator = m + 'G') menu = _._createMenu('&Game') menu.add_command(label = 'S&tatus...', command = _.mStatus, accelerator = 'T') menu.add_checkbutton(label = '&Comments...', variable = _.tkopt.comment, command = _.mEditGameComment) menu.add_separator() submenu = MfxMenu(menu, label = '&Statistics') submenu.add_command(label = 'Current game...', command = (lambda _ = _: _.mPlayerStats(mode = 101))) submenu.add_command(label = 'All games...', command = (lambda _ = _: _.mPlayerStats(mode = 102))) submenu.add_separator() submenu.add_command(label = 'Session log...', command = (lambda _ = _: _.mPlayerStats(mode = 104))) submenu.add_command(label = 'Full log...', command = (lambda _ = _: _.mPlayerStats(mode = 103))) submenu = MfxMenu(menu, label = 'Demo statistics') submenu.add_command(label = 'Current game...', command = (lambda _ = _: _.mPlayerStats(mode = 1101))) submenu.add_command(label = 'All games...', command = (lambda _ = _: _.mPlayerStats(mode = 1102))) menu = _._createMenu('&Assist') menu.add_command(label = '&Hint', command = _.mHint, accelerator = 'H') menu.add_separator() menu.add_command(label = '&Demo', command = _.mDemo, accelerator = m + 'D') menu.add_command(label = 'Demo (&all games)', command = _.mMixedDemo) menu = _._createMenu('&Options') menu.add_command(label = '&Player options...', command = _.mOptPlayerOptions) if PACKAGE == 'PyJongg': submenu = MfxMenu(menu, label = 'Assist &level') submenu.add_checkbutton(label = 'Enable &undo', variable = _.tkopt.undo, command = _.mOptEnableUndo) submenu.add_checkbutton(label = 'Enable &bookmarks', variable = _.tkopt.bookmarks, command = _.mOptEnableBookmarks) submenu.add_checkbutton(label = 'Enable &hint', variable = _.tkopt.hint, command = _.mOptEnableHint) submenu.add_checkbutton(label = 'Enable highlight t&iles', variable = _.tkopt.highlight_piles, command = _.mOptEnableHighlightPiles) else: submenu = MfxMenu(menu, label = '&Automatic play') submenu.add_checkbutton(label = 'Auto &face up', variable = _.tkopt.autofaceup, command = _.mOptAutoFaceUp) submenu.add_checkbutton(label = '&Auto drop', variable = _.tkopt.autodrop, command = _.mOptAutoDrop) submenu.add_checkbutton(label = 'Auto &deal', variable = _.tkopt.autodeal, command = _.mOptAutoDeal) submenu.add_separator() submenu.add_checkbutton(label = '&Quick play', variable = _.tkopt.quickplay, command = _.mOptQuickPlay) submenu = MfxMenu(menu, label = 'Assist &level') submenu.add_checkbutton(label = 'Enable &undo', variable = _.tkopt.undo, command = _.mOptEnableUndo) submenu.add_checkbutton(label = 'Enable &bookmarks', variable = _.tkopt.bookmarks, command = _.mOptEnableBookmarks) submenu.add_checkbutton(label = 'Enable &hint', variable = _.tkopt.hint, command = _.mOptEnableHint) submenu.add_checkbutton(label = 'Enable highlight p&iles', variable = _.tkopt.highlight_piles, command = _.mOptEnableHighlightPiles) submenu.add_checkbutton(label = 'Enable highlight &cards', variable = _.tkopt.highlight_cards, command = _.mOptEnableHighlightCards) submenu.add_checkbutton(label = 'Enable highlight same &rank', variable = _.tkopt.highlight_samerank, command = _.mOptEnableHighlightSameRank) menu.add_separator() if _.app.audio.audiodev is None: menu.add_checkbutton(label = '&Sound', variable = _.tkopt.sound, command = _.mOptSound, state = Tkinter.DISABLED) elif pysolsoundserver: menu.add_checkbutton(label = '&Sound...', variable = _.tkopt.sound, command = _.mOptSoundDialog) else: menu.add_checkbutton(label = '&Sound', variable = _.tkopt.sound, command = _.mOptSound) manager = _.app.cardset_manager n = manager.len() if PACKAGE == 'PyJongg': menu.add_command(label = 'Tiles&et...', command = _.mSelectCardsetDialog, accelerator = 'E') submenu = MfxMenu(menu, label = 'Tile &border') menu.add_command(label = 'Table b&ackground...', command = _.mSelectTileDialog) else: menu.add_command(label = 'Cards&et...', command = _.mSelectCardsetDialog, accelerator = 'E') submenu = MfxMenu(menu, label = 'Card &background') menu.add_command(label = 'Table t&ile...', command = _.mSelectTileDialog) submenu = MfxMenu(menu, label = 'A&nimations') submenu.add_radiobutton(label = '&None', variable = _.tkopt.animations, value = 0, command = _.mOptAnimations) submenu.add_radiobutton(label = '&Timer based', variable = _.tkopt.animations, value = 2, command = _.mOptAnimations) submenu.add_radiobutton(label = '&Fast', variable = _.tkopt.animations, value = 1, command = _.mOptAnimations) submenu.add_radiobutton(label = '&Slow', variable = _.tkopt.animations, value = 3, command = _.mOptAnimations) submenu.add_radiobutton(label = '&Very slow', variable = _.tkopt.animations, value = 4, command = _.mOptAnimations) if PACKAGE != 'PyJongg': menu.add_checkbutton(label = 'Card shado&w', variable = _.tkopt.shadow, command = _.mOptShadow) menu.add_checkbutton(label = 'Shade &legal moves', variable = _.tkopt.shade, command = _.mOptShade) menu.add_separator() menu.add_command(label = '&Hint options...', command = _.mOptHintOptions) menu.add_command(label = '&Demo options...', command = _.mOptDemoOptions) menu.add_separator() submenu = MfxMenu(menu, label = '&Toolbar') submenu.add_radiobutton(label = '&Hide', variable = _.tkopt.toolbar, value = 0, command = _.mOptToolbar) submenu.add_radiobutton(label = '&Top', variable = _.tkopt.toolbar, value = 1, command = _.mOptToolbar) submenu.add_radiobutton(label = '&Bottom', variable = _.tkopt.toolbar, value = 2, command = _.mOptToolbar) submenu.add_separator() submenu.add_radiobutton(label = '&Small icons', variable = _.tkopt.toolbar_size, value = 0, command = _.mOptToolbarSize) submenu.add_radiobutton(label = '&Large icons', variable = _.tkopt.toolbar_size, value = 1, command = _.mOptToolbarSize) menu.add_checkbutton(label = 'Stat&usbar', variable = _.tkopt.statusbar, command = _.mOptStatusbar) menu = _._createMenu('&Help') menu.add_command(label = '&Contents', command = _.mHelp, accelerator = m + 'F1') menu.add_command(label = '&How to play', command = _.mHelpHowToPlay) menu.add_command(label = '&Rules for this game', command = _.mHelpRules, accelerator = 'F1') if bundle & 4 == 0: menu.add_command(label = '&License terms', command = _.mHelpLicense) menu.add_command(label = "What's &new ?", command = _.mHelpNews) if os.name == 'nt': menu.add_separator() menu.add_command(label = PACKAGE + ' &Web Site', command = _.mHelpWebSite) menu.add_separator() menu.add_command(label = '&About ' + PACKAGE + '...', command = _.mHelpAbout) if os.name == 'mac': menu = _._createMenu('Apple') menu.add_command(label = 'About ' + PACKAGE + '...', command = _.mHelpAbout) menu.add_command(label = PACKAGE + ' help', command = _.mHelp) MfxMenubar.addPath = None ctrl = 'Control-' if os.name == 'mac': ctrl = 'Command-' _._bindKey('', 'n', _.mNewGame) _._bindKey('', 'g', _.mSelectGameDialog) _._bindKey('', 'v', _.mSelectGameDialogWithPreview) _._bindKey(ctrl, 'r', _.mSelectRandomGame) _._bindKey(ctrl, 'm', _.mSelectGameById) _._bindKey(ctrl, 'n', _.mNewGameWithNextId) _._bindKey(ctrl, 'o', _.mOpen) _._bindKey(ctrl, 's', _.mSave) _._bindKey(ctrl, 'q', _.mQuit) _._bindKey('', 'z', _.mUndo) _._bindKey('', 'BackSpace', _.mUndo) _._bindKey('', 'r', _.mRedo) _._bindKey(ctrl, 'g', _.mRestart) _._bindKey('', 'space', _.mDeal) _._bindKey('', 't', _.mStatus) _._bindKey(ctrl, 't', _.mPlayerStats) _._bindKey('', 'h', _.mHint) _._bindKey(ctrl, 'h', _.mHint1) _._bindKey('', 'i', _.mHighlightPiles) _._bindKey(ctrl, 'd', _.mDemo) _._bindKey('', 'e', _.mSelectCardsetDialog) _._bindKey(ctrl, 'b', _.mOptChangeCardback) _._bindKey(ctrl, 'i', _.mOptChangeTableTile) _._bindKey(ctrl, 'p', _.mOptPlayerOptions) _._bindKey(ctrl, 'F1', _.mHelp) _._bindKey('', 'F1', _.mHelpRules) _._bindKey('', 'Print', _.mScreenshot) _._bindKey(ctrl, 'u', _.mPlayNextMusic) _._bindKey('', 'a', _.mDrop) _._bindKey(ctrl, 'a', _.mDrop1) _._bindKey('', 's', _.mUndo) _._bindKey('', 'd', _.mDeal) _._bindKey('', 'l', _.mDrop) _._bindKey(ctrl, 'l', _.mDrop1) _._bindKey('', 'k', _.mUndo) _._bindKey('', 'j', _.mDeal) for i in range(9): _._bindKey(ctrl, str(i + 1), (lambda event, _ = _, i = i: _.mGotoBookmark(i, confirm = 0))) def _bindKey(_, modifier, key, func): if 0 and not modifier and len(key) == 1: _._PysolMenubar__keybindings[string.lower(key)] = func _._PysolMenubar__keybindings[string.upper(key)] = func return None sequence = '<' + modifier + 'KeyPress-' + key + '>' try: bind(_.top, sequence, func) if len(key) == 1 and key != string.upper(key): key = string.upper(key) sequence = '<' + modifier + 'KeyPress-' + key + '>' bind(_.top, sequence, func) except: pass def _keyPressHandler(_, event): r = EVENT_PROPAGATE if event and _.game: if _.game.demo: if event.char: _.game.demo.keypress = event.char r = EVENT_HANDLED func = _._PysolMenubar__keybindings.get(event.char) if func and event.state & ~2 == 0: func(event) r = EVENT_HANDLED return r def _createMenu(_, label, tearoff = 0): if os.name == 'posix': tearoff = 1 menu = MfxMenu(_._PysolMenubar__menubar, label, tearoff = tearoff, underline = -1) return menu def _addSelectGameMenu(_, menu): games = map(_.app.gdb.get, _.app.gdb.getGamesIdSortedByShortName()) games = tuple(games) menu.add_command(label = 'All &games...', command = _.mSelectGameDialog, accelerator = 'G') menu.add_command(label = 'Playable pre&view...', command = _.mSelectGameDialogWithPreview, accelerator = 'V') menu.add_separator() data = ('&Popular games', (lambda gi: gi.si.game_flags & GI.GT_POPULAR)) _._addSelectGameSubMenu(menu, games, (data,), _.mSelectGamePopular, _.tkopt.gameid_popular) if PACKAGE == 'PyJongg': menu.add_separator() _._addSelectMahjonggGameSubMenu(menu, games, _.mSelectGame, _.tkopt.gameid) return None submenu = MfxMenu(menu, label = '&Special games') _._addSelectGameSubMenu(submenu, games, GI.SELECT_SPECIAL_GAME_BY_TYPE, _.mSelectGame, _.tkopt.gameid) menu.add_separator() _._addSelectGameSubMenu(menu, games, GI.SELECT_GAME_BY_TYPE, _.mSelectGame, _.tkopt.gameid) def _addSelectGameSubMenu(_, menu, games, select_data, command, variable): need_sep = 0 for label, select_func in select_data: g = filter(select_func, games) if not g: continue if need_sep: menu.add_separator() need_sep = 0 submenu = MfxMenu(menu, label = label) if 1 and label == 'Mahjongg type': pass else: _._addSelectGameSubSubMenu(submenu, g, command, variable) def _addSelectMahjonggGameSubMenu(_, menu, g, command, variable): for c in ('AC', 'DF', 'GK', 'LR', 'ST', 'UZ'): gg = filter((lambda gi, c0 = c[0], c1 = c[1]: None if gi.short_name[0] <= gi.short_name[0] else gi.short_name[0] <= c1), g) label = c[0] if c[0] != c[1]: label = c[0] + ' - ' + c[1] submenu = MfxMenu(menu, label = label) _._addSelectGameSubSubMenu(submenu, gg, command, variable) def _addSelectGameSubSubMenu(_, menu, g, command, variable): cb = (25, _._PysolMenubar__cb_max)[len(g) > 4 * 25] for i in range(len(g)): gi = g[i] if i > 0: pass columnbreak = i % cb == 0 menu.add_radiobutton(command = command, variable = variable, columnbreak = columnbreak, value = gi.id, label = gi.short_name) def _mSelectGameDialog(_, d): if d.status == 0 and d.button == 0 and d.gameid != _.game.id: _.tkopt.gameid.set(d.gameid) _.tkopt.gameid_popular.set(d.gameid) _.game.endGame() _.game.quitGame(d.gameid, random = d.random) return EVENT_HANDLED def __restoreCursor(_, *event): _.game.setCursor(cursor = _.app.top_cursor) def mSelectGameDialog(_, *event): if _._cancelDrag(): return None _.game.setCursor(cursor = CURSOR_WATCH) after_idle(_.top, _._PysolMenubar__restoreCursor) d = SelectGameDialog(_.top, title = 'Select game', app = _.app, gameid = _.game.id) return _._mSelectGameDialog(d) def mSelectGameDialogWithPreview(_, *event): if _._cancelDrag(): return None _.game.setCursor(cursor = CURSOR_WATCH) bookmark = None after_idle(_.top, _._PysolMenubar__restoreCursor) d = SelectGameDialogWithPreview(_.top, title = 'Select game', app = _.app, gameid = _.game.id, bookmark = bookmark) return _._mSelectGameDialog(d) def updateRecentGamesMenu(_, gameids): submenu = _._PysolMenubar__menupath['.menubar.file.recentgames'][2] submenu.delete(0, 'last') cb = (25, _._PysolMenubar__cb_max)[len(gameids) > 4 * 25] i = 0 for id in gameids: gi = _.app.getGameInfo(id) if i > 0: pass columnbreak = i % cb == 0 submenu.add_radiobutton(command = _.mSelectGame, variable = _.tkopt.gameid, columnbreak = columnbreak, value = gi.id, label = gi.short_name) i = i + 1 def updateBookmarkMenuState(_): state = _._getEnabledState mp1 = _._PysolMenubar__menupath.get('.menubar.edit.setbookmark') mp2 = _._PysolMenubar__menupath.get('.menubar.edit.gotobookmark') mp3 = _._PysolMenubar__menupath.get('.menubar.edit.clearbookmarks') if mp1 is None and mp2 is None or mp3 is None: return None if _.app.opt.bookmarks: pass x = _.game.canSetBookmark() (menu, index, submenu) = mp1 for i in range(9): submenu.entryconfig(i, state = state(x)) menu.entryconfig(index, state = state(x)) (menu, index, submenu) = mp2 ms = 0 for i in range(9): s = _.game.gsaveinfo.bookmarks.get(i) is not None if s: pass submenu.entryconfig(i, state = state(x)) if not ms: pass ms = s if ms: pass menu.entryconfig(index, state = state(x)) (menu, index, submenu) = mp3 if ms: pass menu.entryconfig(index, state = state(x)) def updateBackgroundImagesMenu(_): mp = _._PysolMenubar__menupath.get('.menubar.options.cardbackground') if PACKAGE == 'PyJongg' and mp is None: mp = _._PysolMenubar__menupath.get('.menubar.options.tileborder') submenu = mp[2] submenu.delete(0, 'last') mbacks = _.app.images.getCardbacks() cb = int(math.ceil(math.sqrt(len(mbacks)))) for i in range(len(mbacks)): if i > 0: pass columnbreak = i % cb == 0 submenu.add_radiobutton(label = mbacks[i].name, image = mbacks[i].menu_image, variable = _.tkopt.cardback, value = i, command = _.mOptCardback, columnbreak = columnbreak, indicatoron = 0, hidemargin = 0) def setMenuState(_, state, path): path = '.menubar.' + path mp = _._PysolMenubar__menupath.get(path) if PACKAGE == 'PyJongg' and mp is None: return None (menu, index, submenu) = mp s = _._getEnabledState(state) menu.entryconfig(index, state = s) def setToolbarState(_, state, path): s = _._getEnabledState(state) w = getattr(_.app.toolbar, path + '_button') w['state'] = s if PACKAGE == 'PyJongg': DEFAULTEXTENSION = '.pjo' else: DEFAULTEXTENSION = '.pso' FILETYPES = ((PACKAGE + ' files', '*' + DEFAULTEXTENSION), ('All files', '*')) def mOpen(_, *event): if _._cancelDrag(): return None filename = _.game.filename if filename: (idir, ifile) = os.path.split(os.path.normpath(filename)) else: (idir, ifile) = ('', '') if not idir: idir = _.app.dn.savegames d = tkFileDialog.Open() filename = d.show(filetypes = _.FILETYPES, defaultextension = _.DEFAULTEXTENSION, initialdir = idir, initialfile = ifile) if filename: filename = os.path.normpath(filename) if os.path.isfile(filename): _.game.loadGame(filename) def mSaveAs(_, *event): if _._cancelDrag(): return None if not (_.menustate.save_as): return None filename = _.game.filename if not filename: filename = _.app.getGameSaveName(_.game.id) if os.name == 'posix': filename = filename + '-' + _.game.getGameNumber(format = 0) else: filename = filename + '-01' filename = filename + _.DEFAULTEXTENSION (idir, ifile) = os.path.split(os.path.normpath(filename)) if not idir: idir = _.app.dn.savegames d = tkFileDialog.SaveAs() filename = d.show(filetypes = _.FILETYPES, defaultextension = _.DEFAULTEXTENSION, initialdir = idir, initialfile = ifile) if filename: filename = os.path.normpath(filename) _.game.saveGame(filename) _.updateMenus() def mSelectCardsetDialog(_, *event): if _._cancelDrag(): return None (strings, default) = ((None, 'Load', 'Cancel'), 1) if PACKAGE == 'PySol': if os.name == 'posix': (strings, default) = ((None, 'Load', 'Cancel', 'Info...'), 1) t = CARDSET key = _.app.nextgame.cardset.index d = SelectCardsetDialogWithPreview(_.top, title = 'Select ' + t, app = _.app, manager = _.app.cardset_manager, key = key, strings = strings, default = default) cs = _.app.cardset_manager.get(d.key) if cs is None or d.key == _.app.cardset.index: return None if d.status == 0 and d.button in (0, 1) and d.key >= 0: _.app.nextgame.cardset = cs if d.button == 1: _.game.endGame(bookmark = 1) _.game.quitGame(bookmark = 1) def _mOptCardback(_, index): if _._cancelDrag(): return None cs = _.app.cardset old_index = cs.backindex cs.updateCardback(backindex = index) if cs.backindex == old_index: return None _.app.updateCardset(_.game.id) for card in _.game.cards: image = _.app.images.getBack(card.deck, card.suit, card.rank) card.updateCardBackground(image = image) _.app.canvas.update_idletasks() _.tkopt.cardback.set(cs.backindex) def mOptCardback(_, *event): _._mOptCardback(_.tkopt.cardback.get()) def mOptChangeCardback(_, *event): _._mOptCardback(_.app.cardset.backindex + 1) def _mOptTableTile(_, i): if _.app.tabletile_index != i: tile = _.app.tabletile_manager.get(i) if _.game.canvas.setTile(tile.filename): _.game.canvas.setTextColor(tile.text_color) if _.app.debug: _.game.updateStatus(info = tile.basename) _.tkopt.tabletile.set(i) _.app.tabletile_index = i _.app.opt.tabletile_name = tile.basename _.app.tabletile_manager.setSelected(i) def mOptTableTile(_, *event): if _._cancelDrag(): return None _._mOptTableTile(_.tkopt.tabletile.get()) def mOptChangeTableTile(_, *event): if _._cancelDrag(): return None n = _.app.tabletile_manager.len() if n >= 2: i = (_.tkopt.tabletile.get() + 1) % n _._mOptTableTile(i) def mSelectTileDialog(_, *event): if _._cancelDrag(): return None key = _.app.tabletile_index if key <= 0: key = string.lower(_.app.opt.tablecolor) d = SelectTileDialogWithPreview(_.top, title = 'Select table background', app = _.app, manager = _.app.tabletile_manager, key = key) if d.status == 0 and d.button in (0, 1): if type(d.key) is types.StringType: _._mOptTableColor(d.key) elif d.key > 0 and d.key != _.app.tabletile_index: _._mOptTableTile(d.key) def _mOptTableColor(_, color): if _.app.opt.tablecolor != color: _.app.opt.tablecolor = color _.game.canvas.config(bg = color) _.top.config(bg = color) _._mOptTableTile(0) _.game.canvas.setTextColor(None) def mOptTableColor(_, *event): if _._cancelDrag(): return None c = tkColorChooser.askcolor(initialcolor = _.app.opt.tablecolor, title = 'Select table color') if c and c[1]: _._mOptTableColor(c[1]) def mOptToolbar(_, *event): if _._cancelDrag(): return None _.setToolbarSide(_.tkopt.toolbar.get()) def mOptToolbarSize(_, *event): if _._cancelDrag(): return None _.setToolbarSize(_.tkopt.toolbar_size.get()) def mOptToolbarRelief(_, *event): if _._cancelDrag(): return None _.setToolbarRelief(_.tkopt.toolbar_relief.get()) def mOptStatusbar(_, *event): if _._cancelDrag(): return None if not (_.app.statusbar): return None side = _.tkopt.statusbar.get() _.app.opt.statusbar = side if _.app.statusbar.show(side): _.top.update_idletasks() def setToolbarSide(_, side): if _._cancelDrag(): return None _.app.opt.toolbar = side _.tkopt.toolbar.set(side) if _.app.toolbar.show(side): _.top.update_idletasks() def setToolbarSize(_, size): if _._cancelDrag(): return None _.app.opt.toolbar_size = size _.tkopt.toolbar_size.set(size) dir = _.app.getToolbarImagesDir(size) if _.app.toolbar.updateImages(dir, size): _.game.updateStatus(player = _.app.opt.player) _.top.update_idletasks() def setToolbarRelief(_, relief): if _._cancelDrag(): return None _.app.opt.toolbar_relief = relief _.tkopt.toolbar_relief.set(relief) _.top.update_idletasks() class MfxTreeBaseNode: def __init__(_, tree, parent_node, text, key): _.tree = tree _.parent_node = parent_node _.text = text _.key = key _.selected = 0 _.subnodes = None _.symbol_id = -1 _.text_id = -1 _.textrect_id = -1 def registerKey(_): if _.key is not None: l = _.tree.keys.get(_.key, []) l.append(_) _.tree.keys[_.key] = l def whoami(_): if _.parent_node is None: return (_.text,) else: return _.parent_node.whoami() + (_.text,) def draw(_, x, y, lastx = None, lasty = None): (canvas, style) = (_.tree.canvas, _.tree.style) topleftx = x + style.distx toplefty = y - style.height / 2 if lastx is not None: canvas.create_line(x, y, topleftx, y, stipple = style.linestyle, fill = style.linecolor) _.selected = 0 _.symbol_id = -1 _.drawSymbol(topleftx, toplefty) linestart = style.distx + style.width + 5 _.text_id = -1 _.drawText(x + linestart, y) return (x, y, x, y + style.disty) def drawText(_, x, y): (canvas, style) = (_.tree.canvas, _.tree.style) if _.selected: (fg, bg) = (style.text_selected_fg, style.text_selected_bg) else: (fg, bg) = (style.text_normal_fg, style.text_normal_bg) if _.tree.nodes.get(_.text_id) is _: canvas.itemconfig(_.text_id, fill = fg) else: _.text_id = canvas.create_text(x + 1, y, text = _.text, anchor = 'w', justify = 'left', font = style.font, fill = fg) _.tree.nodes[_.text_id] = _ if _.tree.nodes.get(_.textrect_id) is _: canvas.itemconfig(_.textrect_id, fill = bg) elif _.selected: b = canvas.bbox(_.text_id) _.textrect_id = canvas.create_rectangle(b[0] - 1, b[1] - 1, b[2] + 1, b[3] + 1, fill = bg, outline = '') canvas.tag_lower(_.textrect_id, _.text_id) _.tree.nodes[_.textrect_id] = _ def updateText(_): if _.tree.nodes.get(_.text_id) is _: _.drawText(-1, -1) def drawSymbol(_, x, y, **kw): (canvas, style) = (_.tree.canvas, _.tree.style) color = kw.get('color') if color is None: if _.selected: color = 'darkgreen' else: color = 'green' if _.tree.nodes.get(_.symbol_id) is _: canvas.itemconfig(_.symbol_id, fill = color) else: _.symbol_id = canvas.create_rectangle(x + 1, y + 1, x + style.width, y + style.height, fill = color) _.tree.nodes[_.symbol_id] = _ def updateSymbol(_): if _.tree.nodes.get(_.symbol_id) is _: _.drawSymbol(-1, -1) class MfxTreeLeaf(MfxTreeBaseNode): def drawText(_, x, y): if _.text_id < 0: _.registerKey() MfxTreeBaseNode.drawText(_, x, y) class MfxTreeNode(MfxTreeBaseNode): def __init__(_, tree, parent_node, text, key, expanded = 0): MfxTreeBaseNode.__init__(_, tree, parent_node, text, key) _.expanded = expanded def drawChildren(_, x, y, lastx, lasty): _.subnodes = _.tree.getContents(_) (lx, ly) = (lastx, lasty) (nx, ny) = (x, y) for node in _.subnodes: node.tree = _.tree (lx, ly, nx, ny) = node.draw(nx, ny, lx, ly) return ny def draw(_, x, y, ilastx = None, ilasty = None): (lx, ly, nx, ny) = MfxTreeBaseNode.draw(_, x, y, ilastx, ilasty) if _.expanded: style = _.tree.style childx = nx + style.distx + style.width / 2 childy = ny clastx = nx + style.distx + style.width / 2 clasty = ly + style.height / 2 ny = _.drawChildren(childx, childy, clastx, clasty) return (lx, ly, x, ny) def drawSymbol(_, x, y, **kw): color = kw.get('color') if color is None: if _.expanded: color = 'red' else: color = 'pink' MfxTreeBaseNode.drawSymbol(_, x, y, color = color) class MfxTreeInCanvas(MfxScrolledCanvas): class Style: def __init__(_): _.distx = 16 _.disty = 18 _.width = 16 _.height = 16 _.originx = 0 _.originy = 0 _.text_normal_fg = 'black' _.text_normal_bg = 'white' _.text_selected_fg = 'white' _.text_selected_bg = '#00008b' _.font = getFont('tree_small') _.linestyle = 'gray50' _.linecolor = 'black' if os.name == 'nt': _.linestyle = '' _.linecolor = 'gray50' def __init__(_, parent, rootnodes, c_width = 400, c_height = 300, **kw): if not kw.get('bg'): pass bg = kw['bg'] = parent.cget('bg') apply(MfxScrolledCanvas.__init__, (_, parent), kw) _.rootnodes = rootnodes _.updateNodesWithTree(_.rootnodes, _) _.selection_key = None _.nodes = { } _.keys = { } _.style = _.Style() _.style.text_normal_fg = _.canvas.cget('insertbackground') _.style.text_normal_bg = bg _.canvas.config(width = c_width, height = c_height) bind(_.canvas, '<ButtonPress-1>', _.singleClick) bind(_.canvas, '<Double-Button-1>', _.doubleClick) _.frame.pack(fill = Tkinter.BOTH, expand = 1) def destroy(_): for node in _.keys.get(_.selection_key, []): node.selected = 0 MfxScrolledCanvas.destroy(_) def findNode(_, event = None): id = _.canvas.find_withtag(Tkinter.CURRENT) if id: return _.nodes.get(id[0]) return None def draw(_): (nx, ny) = (_.style.originx, _.style.originy) nx = nx - _.style.distx ny = ny + _.style.height / 2 for node in _.rootnodes: node.tree = _ (lx, ly) = (None, None) (lx, ly, nx, ny) = node.draw(nx, ny, lx, ly) bbox = _.canvas.bbox('all') _.canvas.config(scrollregion = (0, 0, bbox[2], bbox[3])) _.canvas.config(yscrollincrement = _.style.disty) _.showVbar() _.showHbar() def clear(_): _.nodes = { } _.keys = { } _.canvas.delete('all') def redraw(_): oldcur = _.canvas['cursor'] _.canvas['cursor'] = 'watch' _.canvas.update_idletasks() _.clear() _.draw() _.updateSelection(_.selection_key) _.showVbar() _.showHbar() _.canvas['cursor'] = oldcur def getContents(_, node): pass def singleClick(_, event = None): pass def doubleClick(_, event = None): _.singleClick(event) def updateSelection(_, key): l1 = _.keys.get(_.selection_key, []) l2 = _.keys.get(key, []) for node in l1: pass for node in l2: pass _.selection_key = key def updateNodesWithTree(_, nodes, tree): for node in nodes: node.tree = tree class SelectDialogTreeLeaf(MfxTreeLeaf): def drawSymbol(_, x, y, **kw): if _.tree.nodes.get(_.symbol_id) is not _: _.symbol_id = _.tree.canvas.create_image(x, y, image = _.tree.data.img[2 + (_.key is None)], anchor = 'nw') _.tree.nodes[_.symbol_id] = _ class SelectDialogTreeNode(MfxTreeNode): def __init__(_, tree, text, select_func, expanded = 0, parent_node = None): MfxTreeNode.__init__(_, tree, parent_node, text, key = None, expanded = expanded) _.select_func = select_func def drawSymbol(_, x, y, **kw): if _.tree.nodes.get(_.symbol_id) is not _: _.symbol_id = _.tree.canvas.create_image(x, y, image = _.tree.data.img[_.expanded], anchor = 'nw') _.tree.nodes[_.symbol_id] = _ def getContents(_): if _.subnodes is not None: return _.subnodes if type(_.select_func) in (types.TupleType, types.ListType): return _.select_func return _._getContents() def _getContents(_): return [] class SelectDialogTreeData: img = None def __init__(_): _.tree_xview = (0.0, 1.0) _.tree_yview = (0.0, 1.0) if _.img is None: SelectDialogTreeData.img = (Tkinter.PhotoImage(data = '\nR0lGODlhEAAOAPIFAAAAAICAgMDAwP//AP///4AAAAAAAAAAACH5BAEAAAUALAAAAAAQAA4AAAOL\nWLrcGxA6FoYYYoRZwhCDMAhDFCkBoa6sGgBFQAzCIAzCIAzCEACFAEEwEAwEA8FAMBAEAIUAYSAY\nCAaCgWAgGAQAhQBBMBAMBAPBQDAQBACFAGEgGAgGgoFgIBgEAAUBBAIDAgMCAwIDAgMCAQAFAQQD\nAgMCAwIDAgMCAwEABSaiogAKAKeoqakFCQA7'), Tkinter.PhotoImage(data = '\nR0lGODlhEAAOAPIFAAAAAICAgMDAwP//AP///4AAAAAAAAAAACH5BAEAAAUALAAAAAAQAA4AAAN3\nWLrcHBA6Foi1YZZAxBCDQESREhCDMAiDcFkBUASEMAiDMAiDMAgBAGlIGgQAgZeSEAAIAoAAQTAQ\nDAQDwUAwAEAAhQBBMBAMBAPBQBAABACFAGEgGAgGgoFgIAAEAAoBBAMCAwIDAgMCAwEAAApERI4L\njpWWlgkAOw=='), Tkinter.PhotoImage(data = '\nR0lGODdhEAAOAPIAAAAAAAAAgICAgMDAwP///wAAAAAAAAAAACwAAAAAEAAOAAADTii63DowyiiA\nGCHrnQUQAxcQAAEQgAAIg+MCwkDMdD0LgDDUQG8LAMGg1gPYBADBgFbs1QQAwYDWBNQEAMHABrAR\nBADBwOsVAFzoqlqdAAA7'), Tkinter.PhotoImage(data = '\nR0lGODdhEAAOAPIAAAAAAAAAgICAgMDAwP8AAP///wAAAAAAACwAAAAAEAAOAAADVCi63DowyiiA\nGCHrnQUQAxcUQAEUgAAIg+MCwlDMdD0LgDDQBE3UAoBgUCMUCDYBQDCwEWwFAUAwqBEKBJsAIBjQ\nCDRCTQAQDKBQAcDFBrjf8Lg7AQA7')) class SelectDialogTreeCanvas(MfxTreeInCanvas): def __init__(_, dialog, parent, key, default, font = None, width = -1, height = -1, vbar = 1, hbar = 0): _.dialog = dialog _.default = default _.n_selections = 0 _.n_expansions = 0 disty = 16 if width < 0: width = 400 if height < 0: height = 20 * disty if parent and parent.winfo_screenheight() >= 600: height = 25 * disty if parent and parent.winfo_screenheight() >= 800: height = 30 * disty _.lines = height / disty MfxTreeInCanvas.__init__(_, parent, _.data.rootnodes, c_width = width, c_height = height, vbar = vbar, hbar = hbar) _.style.distx = 20 _.style.disty = disty _.style.width = 16 _.style.height = 14 if font: _.style.font = font _.draw() _.updateSelection(key) if _.hbar: _.canvas.xview_moveto(_.data.tree_xview[0]) if _.vbar: _.canvas.yview_moveto(_.data.tree_yview[0]) _.showVbar() _.showHbar() def destroy(_): if _.n_expansions > 0: _.data.tree_xview = _.canvas.xview() _.data.tree_yview = _.canvas.yview() MfxTreeInCanvas.destroy(_) def getContents(_, node): return node.getContents() def singleClick(_, event = None): node = _.findNode() if isinstance(node, MfxTreeLeaf): if not (node.selected) and node.key is not None: oldcur = _.canvas['cursor'] _.canvas['cursor'] = 'watch' _.canvas.update_idletasks() _.n_selections = _.n_selections + 1 _.updateSelection(node.key) _.dialog.updatePreview(_.selection_key) _.canvas['cursor'] = oldcur elif isinstance(node, MfxTreeNode): _.n_expansions = _.n_expansions + 1 node.expanded = not (node.expanded) _.redraw() return 'break' def doubleClick(_, event = None): node = _.findNode() if isinstance(node, MfxTreeLeaf): if node.key is not None: _.n_selections = _.n_selections + 1 _.updateSelection(node.key) _.dialog.mDone(_.default) elif isinstance(node, MfxTreeNode): _.n_expansions = _.n_expansions + 1 node.expanded = not (node.expanded) _.redraw() return 'break' class SelectDialogPreviewCanvas(MfxScrolledCanvas): def createCanvas(_, kw): _.canvas = apply(MfxCanvas, (_.frame,), kw) _.canvas.grid(row = 0, column = 0, sticky = 'nsew') def pack(_, kw): _.frame.pack(side = 'right', fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady) def setup(_, app, kw): _.canvas.config(bg = app.opt.tablecolor) tile = app.tabletile_manager.get(app.tabletile_index) if tile: _.canvas.setTile(tile.filename) _.canvas.setTextColor(tile.text_color) _.pack(kw) class SelectGameLeaf(SelectDialogTreeLeaf): pass class SelectGameNode(SelectDialogTreeNode): def _getContents(_): contents = [] if not contents: pass return _.tree.data.no_games class SelectGameData(SelectDialogTreeData): def __init__(_, app): SelectDialogTreeData.__init__(_) _.all_games_gi = map(app.gdb.get, app.gdb.getGamesIdSortedByName()) _.no_games = [ SelectGameLeaf(None, None, '(no games)', None)] s_by_type = s_special = s_original = s_contrib = None g = [] for data in (GI.SELECT_GAME_BY_TYPE, GI.SELECT_SPECIAL_GAME_BY_TYPE, GI.SELECT_ORIGINAL_GAME_BY_TYPE, GI.SELECT_CONTRIB_GAME_BY_TYPE): gg = [] for name, select_func in data: name = re.sub('&', '', name) gg.append(SelectGameNode(None, name, select_func)) g.append(gg) if 1 and g[1]: s_special = SelectGameNode(None, 'Special Games', tuple(g[1])) if 1 and g[2]: s_original = SelectGameNode(None, 'Original Games', tuple(g[2])) if 1 and g[3]: pass (s_by_compatibility, gg) = (None, []) for name, games in GI.GAMES_BY_COMPATIBILITY: select_func = lambda gi, games = games: gi.id in games gg.append(SelectGameNode(None, name, select_func)) if 1 and gg: s_by_compatibility = SelectGameNode(None, 'by Compatibility', tuple(gg)) (s_by_pysol_version, gg) = (None, []) for name, games in GI.GAMES_BY_PYSOL_VERSION: select_func = lambda gi, games = games: gi.id in games name = 'New games in v' + name gg.append(SelectGameNode(None, name, select_func)) if 1 and gg and PACKAGE == 'PySol': s_by_pysol_version = SelectGameNode(None, 'by PySol version', tuple(gg)) ul_alternate_names = UserList(list(app.gdb.getGamesTuplesSortedByAlternateName())) _.rootnodes = filter(None, (SelectGameNode(None, 'All Games', (lambda gi: 1), expanded = 0), SelectGameNode(None, 'Alternate Names', ul_alternate_names), SelectGameNode(None, 'Popular Games', (lambda gi: gi.si.game_flags & GI.GT_POPULAR), expanded = 0), s_special, s_by_type, SelectGameNode(None, 'by Game Feature', (SelectGameNode(None, 'by Number of Cards', (SelectGameNode(None, '32 cards', (lambda gi: gi.si.ncards == 32)), SelectGameNode(None, '48 cards', (lambda gi: gi.si.ncards == 48)), SelectGameNode(None, '52 cards', (lambda gi: gi.si.ncards == 52)), SelectGameNode(None, '64 cards', (lambda gi: gi.si.ncards == 64)), SelectGameNode(None, '78 cards', (lambda gi: gi.si.ncards == 78)), SelectGameNode(None, '104 cards', (lambda gi: gi.si.ncards == 104)), SelectGameNode(None, '144 cards', (lambda gi: gi.si.ncards == 144)), SelectGameNode(None, 'Other number', (lambda gi: gi.si.ncards not in (32, 48, 52, 64, 78, 104, 144))))), SelectGameNode(None, 'by Number of Decks', (SelectGameNode(None, '1 deck games', (lambda gi: gi.si.decks == 1)), SelectGameNode(None, '2 deck games', (lambda gi: gi.si.decks == 2)), SelectGameNode(None, '3 deck games', (lambda gi: gi.si.decks == 3)), SelectGameNode(None, '4 deck games', (lambda gi: gi.si.decks == 4)))), SelectGameNode(None, 'by Number of Redeals', (SelectGameNode(None, 'No redeal', (lambda gi: gi.si.redeals == 0)), SelectGameNode(None, '1 redeal', (lambda gi: gi.si.redeals == 1)), SelectGameNode(None, '2 redeals', (lambda gi: gi.si.redeals == 2)), SelectGameNode(None, '3 redeals', (lambda gi: gi.si.redeals == 3)), SelectGameNode(None, 'Unlimited redeals', (lambda gi: gi.si.redeals == -1)), SelectGameNode(None, 'Other number', (lambda gi: gi.si.redeals not in (-1, 0, 1, 2, 3))))), s_by_compatibility)), s_by_pysol_version, SelectGameNode(None, 'Other Categories', (SelectGameNode(None, 'Games for Children (very easy)', (lambda gi: gi.si.game_flags & GI.GT_CHILDREN)), SelectGameNode(None, 'Games with Scoring', (lambda gi: gi.si.game_flags & GI.GT_SCORE)), SelectGameNode(None, 'Games with Separate Decks', (lambda gi: gi.si.game_flags & GI.GT_SEPARATE_DECKS)), SelectGameNode(None, 'Open Games (all cards visible)', (lambda gi: gi.si.game_flags & GI.GT_OPEN)), SelectGameNode(None, 'Relaxed Variants', (lambda gi: gi.si.game_flags & GI.GT_RELAXED)))), s_original, s_contrib)) class SelectGameTreeWithPreview(SelectDialogTreeCanvas): data = None html_viewer = None class SelectGameTree(SelectGameTreeWithPreview): def singleClick(_, event = None): _.doubleClick(event) class SelectGameDialog(MfxDialog): Tree_Class = SelectGameTree TreeDataHolder_Class = SelectGameTreeWithPreview TreeData_Class = SelectGameData def __init__(_, parent, title, app, gameid, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.gameid = gameid _.app = app _.random = None if _.TreeDataHolder_Class.data is None: _.TreeDataHolder_Class.data = _.TreeData_Class(app) _.top.wm_minsize(200, 200) _.tree = _.Tree_Class(_, top_frame, key = gameid, default = kw.default, font = kw.font, vbar = 1, hbar = 0) _.tree.frame.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady) focus = _.createButtons(bottom_frame, kw) focus = _.tree.frame _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, strings = (None, None, 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5) return MfxDialog.initKw(_, kw) def destroy(_): _.app = None _.tree.updateNodesWithTree(_.tree.rootnodes, None) _.tree.destroy() MfxDialog.destroy(_) def mDone(_, button): if button == 0: _.gameid = _.tree.selection_key _.tree.n_expansions = 1 if button == 1: doc = _.app.getGameRulesFilename(_.tree.selection_key) if not doc: return None dir = os.path.join('html', 'rules') viewer = SelectGameTreeWithPreview.html_viewer if viewer is None: SelectGameTreeWithPreview.html_viewer = helpHTML(_.app, doc, dir) else: url = _.app.dataloader.findFile(doc, dir) viewer.updateHistoryXYView() viewer.display(url, relpath = 0) return None MfxDialog.mDone(_, button) class SelectGameDialogWithPreview(SelectGameDialog): Tree_Class = SelectGameTreeWithPreview def __init__(_, parent, title, app, gameid, bookmark = None, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.app = app _.gameid = gameid _.bookmark = bookmark _.random = None if _.TreeDataHolder_Class.data is None: _.TreeDataHolder_Class.data = _.TreeData_Class(app) _.top.wm_minsize(400, 200) sw = _.top.winfo_screenwidth() if sw >= 1100: (w1, w2) = (250, 600) elif sw >= 900: (w1, w2) = (250, 500) elif sw >= 800: (w1, w2) = (220, 480) else: (w1, w2) = (200, 300) w2 = max(200, min(w2, 10 + 12 * (app.subsampled_images.CARDW + 10))) _.tree = _.Tree_Class(_, top_frame, key = gameid, default = kw.default, font = kw.font, width = w1, vbar = 1, hbar = 0) _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady) _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, vbar = 1, hbar = 1) _.preview.setup(app, kw) _.preview.canvas.preview = 2 _.preview_key = -1 _.preview_game = None _.preview_app = None _.updatePreview(gameid, animations = 0) focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, strings = ('Select', None, 'Cancel'), default = 0) return SelectGameDialog.initKw(_, kw) def destroy(_): _.deletePreview(destroy = 1) _.preview.unbind() SelectGameDialog.destroy(_) def deletePreview(_, destroy = 0): _.preview_key = -1 if _.preview: unbind_destroy(_.preview.canvas) _.preview.canvas.deleteAllItems() if destroy: _.preview.canvas.delete('all') if _.preview_game: _.preview_game.endGame() _.preview_game.destruct() destruct(_.preview_game) _.preview_game = None if destroy: if _.preview_app: destruct(_.preview_app) _.preview_app = None def updatePreview(_, gameid, animations = 5): if gameid == _.preview_key: return None _.deletePreview() canvas = _.preview.canvas gi = _.app.gdb.get(gameid) if not gi: _.preview_key = -1 return None if _.preview_app is None: _.preview_app = Struct(audio = _.app.audio, canvas = canvas, cardset = _.app.cardset.copy(), comments = _.app.comments.new(), debug = 0, gamerandom = _.app.gamerandom, gdb = _.app.gdb, gimages = _.app.gimages, images = _.app.subsampled_images, menubar = None, miscrandom = _.app.miscrandom, opt = _.app.opt.copy(), startup_opt = _.app.startup_opt, stats = _.app.stats.new(), top = None, top_cursor = _.app.top_cursor, toolbar = None, constructGame = _.app.constructGame) _.preview_app.opt.shadow = 0 _.preview_app.opt.shade = 0 _.preview_app.audio = None if animations >= 0: _.preview_app.opt.animations = animations if _.preview_game: _.preview_game.endGame() _.preview_game.destruct() _.top.wm_title('Playable Preview - ' + _.app.getGameTitleName(gameid)) if 1: (cw, ch) = (canvas.winfo_width(), canvas.winfo_height()) if cw >= 100 and ch >= 100: MfxCanvasText(canvas, cw / 2, ch - 4, preview = 0, anchor = 's', text = 'Playable Area', font = getFont('canvas_large')) _.preview_game = gi.gameclass(gi) _.preview_game.createPreview(_.preview_app) (tx, ty) = (0, 0) (gw, gh) = (_.preview_game.width, _.preview_game.height) canvas.config(scrollregion = (-tx, -ty, -tx, -ty)) canvas.xview_moveto(0) canvas.yview_moveto(0) random = None if gameid == _.gameid: random = _.app.game.random.copy() if gameid == _.gameid and _.bookmark: _.preview_game.restoreGameFromBookmark(_.bookmark) else: _.preview_game.newGame(random = random, autoplay = 1) canvas.config(scrollregion = (-tx, -ty, gw, gh)) _.preview.showVbar() _.preview.showHbar() _.preview_app.audio = _.app.audio if _.app.opt.animations: _.preview_app.opt.animations = 5 else: _.preview_app.opt.animations = 0 _.random = _.preview_game.random.copy() _.random.origin = _.random.ORIGIN_PREVIEW _.preview_key = gameid class SelectCardsetLeaf(SelectDialogTreeLeaf): pass class SelectCardsetNode(SelectDialogTreeNode): def _getContents(_): contents = [] for obj in _.tree.data.all_objects: pass if not contents: pass return _.tree.data.no_contents class SelectCardsetData(SelectDialogTreeData): def __init__(_, manager, key): SelectDialogTreeData.__init__(_) _.all_objects = manager.getAllSortedByName() _.all_objects = filter((lambda obj: not (obj.error)), _.all_objects) _.no_contents = [ SelectCardsetLeaf(None, None, '(no cardsets)', key = None)] select_by_type = None items = CSI.TYPE.items() items.sort((lambda a, b: cmp(a[1], b[1]))) nodes = [] for key, name in items: pass if nodes: select_by_type = SelectCardsetNode(None, 'by Type', tuple(nodes), expanded = 1) select_by_style = None items = CSI.STYLE.items() items.sort((lambda a, b: cmp(a[1], b[1]))) nodes = [] for key, name in items: pass if nodes: nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.styles)))) select_by_style = SelectCardsetNode(None, 'by Style', tuple(nodes)) select_by_nationality = None items = CSI.NATIONALITY.items() items.sort((lambda a, b: cmp(a[1], b[1]))) nodes = [] for key, name in items: pass if nodes: nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.nationalities)))) select_by_nationality = SelectCardsetNode(None, 'by Nationality', tuple(nodes)) select_by_date = None items = CSI.DATE.items() items.sort((lambda a, b: cmp(a[1], b[1]))) nodes = [] for key, name in items: pass if nodes: nodes.append(SelectCardsetNode(None, 'Uncategorized', (lambda cs: not (cs.si.dates)))) select_by_date = SelectCardsetNode(None, 'by Date', tuple(nodes)) _.rootnodes = filter(None, (SelectCardsetNode(None, 'All Cardsets', (lambda cs: 1), expanded = len(_.all_objects) <= 12), SelectCardsetNode(None, 'by Size', (SelectCardsetNode(None, 'Tiny cardsets', (lambda cs: cs.si.size == CSI.SIZE_TINY)), SelectCardsetNode(None, 'Small cardsets', (lambda cs: cs.si.size == CSI.SIZE_SMALL)), SelectCardsetNode(None, 'Medium cardsets', (lambda cs: cs.si.size == CSI.SIZE_MEDIUM)), SelectCardsetNode(None, 'Large cardsets', (lambda cs: cs.si.size == CSI.SIZE_LARGE)), SelectCardsetNode(None, 'XLarge cardsets', (lambda cs: cs.si.size == CSI.SIZE_XLARGE))), expanded = 1), select_by_type, select_by_style, select_by_date, select_by_nationality)) class SelectCardsetByTypeData(SelectDialogTreeData): def __init__(_, manager, key): SelectDialogTreeData.__init__(_) _.all_objects = manager.getAllSortedByName() _.no_contents = [ SelectCardsetLeaf(None, None, '(no cardsets)', key = None)] items = CSI.TYPE.items() items.sort((lambda a, b: cmp(a[1], b[1]))) nodes = [] for key, name in items: pass select_by_type = SelectCardsetNode(None, 'by Type', tuple(nodes), expanded = 1) _.rootnodes = filter(None, (select_by_type,)) class SelectCardsetTree(SelectDialogTreeCanvas): data = None class SelectCardsetByTypeTree(SelectDialogTreeCanvas): data = None class SelectCardsetDialogWithPreview(MfxDialog): Tree_Class = SelectCardsetTree TreeDataHolder_Class = SelectCardsetTree TreeData_Class = SelectCardsetData def __init__(_, parent, title, app, manager, key = None, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) if key is None: key = manager.getSelected() _.manager = manager _.key = key if _.TreeDataHolder_Class.data is None: _.TreeDataHolder_Class.data = _.TreeData_Class(manager, key) _.top.wm_minsize(400, 200) if _.top.winfo_screenwidth() >= 800: (w1, w2) = (200, 400) else: (w1, w2) = (200, 300) _.tree = _.Tree_Class(_, top_frame, key = key, default = kw.default, font = kw.font, width = w1) _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady) _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, hbar = 1) _.preview.setup(app, kw) _.preview_key = -1 _.preview_images = [] _.updatePreview(key) focus = _.createButtons(bottom_frame, kw) focus = _.tree.frame _.mainloop(focus, kw.timeout) def destroy(_): _.tree.updateNodesWithTree(_.tree.rootnodes, None) _.tree.destroy() _.preview.unbind() _.preview_images = [] MfxDialog.destroy(_) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Load', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5) return MfxDialog.initKw(_, kw) def mDone(_, button): if button in (0, 1): _.key = _.tree.selection_key _.tree.n_expansions = 1 if button in (3, 4): cs = _.manager.get(_.tree.selection_key) if not cs: return None f = os.path.join(cs.dir, 'COPYRIGHT') if button == 4: f = os.path.join(cs.dir, 'config.txt') try: file = open(f) text = file.read() file.close() except: return None d = DisplayTextDialog(_.top, title = CARDSET + ' ' + cs.name, text = text) return None MfxDialog.mDone(_, button) def updatePreview(_, key): if key == _.preview_key: return None canvas = _.preview.canvas canvas.deleteAllItems() _.preview_images = [] cs = _.manager.get(key) if not cs: _.preview_key = -1 return None (names, columns) = cs.getPreviewCardNames() try: (names, columns) = cs.getPreviewCardNames() for n in names: f = os.path.join(cs.dir, n + cs.ext) _.preview_images.append(Tkinter.PhotoImage(file = f)) except: _.preview_key = -1 _.preview_images = [] return None (i, x, y, sx, sy, dx, dy) = (0, 10, 10, 0, 0, cs.CARDW + 10, cs.CARDH + 10) for image in _.preview_images: MfxCanvasImage(canvas, x, y, anchor = 'nw', image = image) (sx, sy) = (max(x, sx), max(y, sy)) i = i + 1 canvas.config(scrollregion = (0, 0, sx + dx, sy + dy)) canvas.config(xscrollincrement = dx, yscrollincrement = dy) _.preview.showVbar() _.preview.showHbar() _.preview_key = key class SelectCardsetByTypeDialogWithPreview(SelectCardsetDialogWithPreview): Tree_Class = SelectCardsetByTypeTree TreeDataHolder_Class = SelectCardsetByTypeTree TreeData_Class = SelectCardsetByTypeData class SelectTileLeaf(SelectDialogTreeLeaf): pass class SelectTileNode(SelectDialogTreeNode): def _getContents(_): contents = [] for obj in _.tree.data.all_objects: pass if not contents: pass return _.tree.data.no_contents class SelectTileData(SelectDialogTreeData): def __init__(_, manager, key): SelectDialogTreeData.__init__(_) _.all_objects = manager.getAllSortedByName() _.all_objects = filter((lambda obj: not (obj.error)), _.all_objects) _.all_objects = filter((lambda tile: if tile.index > 0: passtile.filename), _.all_objects) _.no_contents = [ SelectTileLeaf(None, None, '(no tiles)', key = None)] if not type(key) is types.StringType: pass e1 = len(_.all_objects) <= 17 e2 = 1 _.rootnodes = (SelectTileNode(None, 'Solid Colors', (SelectTileLeaf(None, None, 'Blue', key = '#0082df'), SelectTileLeaf(None, None, 'Green', key = '#008200'), SelectTileLeaf(None, None, 'Navy', key = '#000086'), SelectTileLeaf(None, None, 'Olive', key = '#868200'), SelectTileLeaf(None, None, 'Orange', key = '#f79600'), SelectTileLeaf(None, None, 'Teal', key = '#008286')), expanded = e1), SelectTileNode(None, 'All Backgrounds', (lambda tile: 1), expanded = e2)) class SelectTileTree(SelectDialogTreeCanvas): data = None class SelectTileDialogWithPreview(MfxDialog): Tree_Class = SelectTileTree TreeDataHolder_Class = SelectTileTree TreeData_Class = SelectTileData def __init__(_, parent, title, app, manager, key = None, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) if key is None: key = manager.getSelected() _.manager = manager _.key = key _.tablecolor = app.opt.tablecolor if _.TreeDataHolder_Class.data is None: _.TreeDataHolder_Class.data = _.TreeData_Class(manager, key) _.top.wm_minsize(400, 200) if _.top.winfo_screenwidth() >= 800: (w1, w2) = (200, 400) else: (w1, w2) = (200, 300) _.tree = _.Tree_Class(_, top_frame, key = key, default = kw.default, font = kw.font, width = w1) _.tree.frame.pack(side = 'left', fill = Tkinter.BOTH, expand = 0, padx = kw.padx, pady = kw.pady) _.preview = SelectDialogPreviewCanvas(top_frame, width = w2, vbar = 0, hbar = 0) _.preview.pack(kw) _.preview_key = -1 _.updatePreview(key) focus = _.createButtons(bottom_frame, kw) focus = _.tree.frame _.mainloop(focus, kw.timeout) def destroy(_): _.tree.updateNodesWithTree(_.tree.rootnodes, None) _.tree.destroy() _.preview.unbind() MfxDialog.destroy(_) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', 'Solid color...', 'Cancel'), default = 0, separatorwidth = 2, resizable = 1, font = None, padx = 10, pady = 10, buttonpadx = 10, buttonpady = 5) return MfxDialog.initKw(_, kw) def mDone(_, button): if button == 0: _.key = _.tree.selection_key _.tree.n_expansions = 1 if button == 1: c = tkColorChooser.askcolor(master = _.top, initialcolor = _.tablecolor, title = 'Select table color') if c and c[1]: _.key = string.lower(c[1]) _.tablecolor = _.key _.tree.updateSelection(_.key) _.updatePreview(_.key) return None MfxDialog.mDone(_, button) def updatePreview(_, key): if key == _.preview_key: return None canvas = _.preview.canvas if type(key) is types.StringType: canvas.config(bg = key) canvas.deleteAllItems() canvas.setTile(None) canvas.setTextColor(None) _.preview_key = key _.tablecolor = key return None im = None tile = _.manager.get(key) if tile: try: im = Tkinter.PhotoImage(file = tile.filename) except Tkinter.TclError: ex = None im = None canvas.deleteAllItems() canvas.setTile(im) canvas.setTextColor(tile.text_color) _.preview_key = key if im is None: _.preview_key = -1 class SingleGame_StatsDialog(MfxDialog): def __init__(_, parent, title, app, player, gameid, **kw): kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.top.wm_minsize(200, 200) _.button = kw.default createChart = _.create3DBarChart if parent.winfo_screenwidth() < 800 or parent.winfo_screenheight() < 600: createChart = _.createPieChart createChart = _.createSimpleChart (won, lost) = app.stats.getStats(player, gameid) createChart(top_frame, app, won, lost, 'Total') g = app.stats.session_games.get(player, []) g = filter((lambda a, b = gameid: a[0] == b), g) won = len(filter((lambda a, b = gameid: a[2] > 0), g)) lost = len(filter((lambda a, b = gameid: a[2] == 0), g)) createChart(top_frame, app, won, lost, 'Current session') focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def _getPwon(_, won, lost): (pwon, plost) = (0.0, 0.0) if won + lost > 0: pwon = float(won) / (won + lost) pwon = min(max(pwon, 1e-05), 0.99999) plost = 1.0 - pwon return (pwon, plost) def _createChartInit(_, frame, w, h, text): tfont = getFont('tree_small') frame = Tkinter.Frame(frame) c = Tkinter.Canvas(frame, width = w, height = h) fg = c.cget('insertbackground') frame.pack(side = Tkinter.TOP, fill = Tkinter.BOTH, expand = 1, padx = 20, pady = 10) c.pack() c.create_rectangle(2, 7, w, h, fill = '', outline = '#7f7f7f') l = Tkinter.Label(c, text = text, font = tfont, bd = 0, padx = 3, pady = 1) c.create_window(20, 0, window = l, anchor = 'nw') return (c, tfont, fg) def _createChartTexts(_, c, tx, ty, won, lost): tfont = getFont('tree_small') fg = c.cget('insertbackground') (pwon, plost) = _._getPwon(won, lost) x = tx[0] c.create_text(x, ty[0], text = 'Won:', anchor = 'nw', font = tfont, fill = fg) c.create_text(x, ty[1], text = 'Lost:', anchor = 'nw', font = tfont, fill = fg) c.create_text(x, ty[2], text = 'Total:', anchor = 'nw', font = tfont, fill = fg) x = tx[1] - 16 c.create_text(x, ty[0], text = '%d' % won, anchor = 'ne', font = tfont, fill = fg) c.create_text(x, ty[1], text = '%d' % lost, anchor = 'ne', font = tfont, fill = fg) c.create_text(x, ty[2], text = '%d' % (won + lost), anchor = 'ne', font = tfont, fill = fg) y = ty[2] - 11 c.create_line(tx[0], y, x, y, fill = fg) if won + lost > 0: x = tx[2] pw = int(round(100.0 * pwon)) c.create_text(x, ty[0], text = '%d%%' % pw, anchor = 'ne', font = tfont, fill = fg) c.create_text(x, ty[1], text = '%d%%' % (100 - pw), anchor = 'ne', font = tfont, fill = fg) def _createChart3DBar(_, canvas, perc, x, y, p, col): if perc < 0.005: return None p = list(p[:]) for i in (0, 1, 2, 3): p[i] = (x + p[i][0], y + p[i][1]) j = i + 4 dx = int(round(p[j][0] * perc)) dy = int(round(p[j][1] * perc)) p[j] = (p[i][0] + dx, p[i][1] + dy) def draw_rect(a, b, c, d, col, canvas = canvas, p = p): points = (p[a][0], p[a][1], p[b][0], p[b][1], p[c][0], p[c][1], p[d][0], p[d][1]) canvas.create_polygon(points, fill = col) draw_rect(0, 1, 5, 4, col[0]) draw_rect(1, 2, 6, 5, col[1]) draw_rect(4, 5, 6, 7, col[2]) def draw_line(a, b, canvas = canvas, p = p): canvas.create_line(p[a][0], p[a][1], p[b][0], p[b][1]) draw_line(0, 1) draw_line(1, 2) draw_line(0, 4) draw_line(1, 5) draw_line(2, 6) draw_line(4, 5) draw_line(5, 6) draw_line(6, 7) draw_line(7, 4) def createSimpleChart(_, frame, app, won, lost, text): (c, tfont, fg) = _._createChartInit(frame, 300, 100, text) tx = (90, 180, 210) ty = (21, 41, 75) _._createChartTexts(c, tx, ty, won, lost) def create3DBarChart(_, frame, app, won, lost, text): image = app.gimages.stats[0] (iw, ih) = (image.width(), image.height()) (c, tfont, fg) = _._createChartInit(frame, iw + 160, ih, text) (pwon, plost) = _._getPwon(won, lost) tx = (iw + 20, iw + 110, iw + 140) yy = ih / 2 ty = (yy + 21 - 46, yy + 41 - 46, yy + 75 - 46) c.create_image(0, 7, image = image, anchor = 'nw') p = ((0, 0), (44, 6), (62, -9), (20, -14), (-3, -118), (-1, -120), (-1, -114), (-4, -112)) col = ('#00ff00', '#008200', '#00c300') _._createChart3DBar(c, pwon, 102, 145 + 7, p, col) p = ((0, 0), (49, 6), (61, -10), (15, -15), (1, -123), (3, -126), (4, -120), (1, -118)) col = ('#ff0000', '#860400', '#c70400') _._createChart3DBar(c, plost, 216, 159 + 7, p, col) _._createChartTexts(c, tx, ty, won, lost) def createPieChart(_, frame, app, won, lost, text): (c, tfont, fg) = _._createChartInit(frame, 300, 100, text) (pwon, plost) = _._getPwon(won, lost) tx = (160, 250, 280) ty = (21, 41, 75) if won + lost > 0: (s, ewon, elost) = (0.0, 360.0 * pwon, 360.0 * plost) c.create_arc(20, 25 + 9, 110, 75 + 9, fill = '#007f00', start = s, extent = ewon) c.create_arc(20, 25 + 9, 110, 75 + 9, fill = '#7f0000', start = s + ewon, extent = elost) c.create_arc(20, 25, 110, 75, fill = '#00ff00', start = s, extent = ewon) c.create_arc(20, 25, 110, 75, fill = '#ff0000', start = s + ewon, extent = elost) (x, y) = (tx[0] - 25, ty[0]) c.create_rectangle(x, y, x + 10, y + 10, fill = '#00ff00') y = ty[1] c.create_rectangle(x, y, x + 10, y + 10, fill = '#ff0000') else: c.create_oval(20, 25 + 10, 110, 75 + 10, fill = '#7f7f7f') c.create_oval(20, 25, 110, 75, fill = '#f0f0f0') c.create_text(65, 50, text = 'No games', anchor = 'center', font = tfont, fill = '#bfbfbf') _._createChartTexts(c, tx, ty, won, lost) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', ('All games...', 102), ('Reset...', 302)), default = 0, separatorwidth = 0, padx = 10, pady = 10) return MfxDialog.initKw(_, kw) def getDefaultFont(_): return getFont('small') class AllGames_StatsDialogScrolledCanvas(MfxScrolledCanvas): pass class AllGames_StatsDialog(MfxDialog): (CHAR_W, CHAR_H) = (7, 16) if os.name == 'mac': CHAR_W = 6 YVIEW = 0 def __init__(_, parent, title, app, player, **kw): lines = 25 if parent and parent.winfo_screenheight() < 600: lines = 20 kwdefault(kw, c_height = lines * _.CHAR_H) kw = _.initKw(kw) _ToplevelDialog.__init__(_, parent, title, kw.resizable, kw.default) (top_frame, bottom_frame) = _.createFrames(kw) _.createBitmaps(top_frame, kw) _.app = app _.top.wm_minsize(200, 200) _.button = kw.default _.font = kw.font _.sc = AllGames_StatsDialogScrolledCanvas(top_frame) _.canvas = _.sc.canvas _.canvas.config(width = kw.c_width, height = kw.c_height) _.sc.frame.pack(fill = Tkinter.BOTH, expand = 1, padx = kw.padx, pady = kw.pady) _.nodes = { } _.canvas.dialog = _ bind(_.canvas, '<ButtonPress-1>', _.singleClick) _.fillCanvas(player, title) bbox = _.canvas.bbox('all') _.canvas.config(scrollregion = (0, 0, bbox[2], bbox[3])) _.canvas.yview_moveto(_.YVIEW) _.sc.showVbar() _.sc.showHbar() focus = _.createButtons(bottom_frame, kw) _.mainloop(focus, kw.timeout) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', ('Save to file', 202), ('Reset all...', 301)), default = 0, separatorwidth = 2, resizable = 1, padx = 10, pady = 10, c_width = 500) return MfxDialog.initKw(_, kw) def getDefaultFont(_): return getFont('small') def destroy(_): _.app = None _.canvas.dialog = None _.nodes = { } _.sc.destroy() MfxDialog.destroy(_) def singleClick(_, event = None): id = _.canvas.find_withtag(Tkinter.CURRENT) if not id: return None (gameid, gamenumber) = _.nodes.get(id[0], (None, None)) return None if gameid and gamenumber: print gameid, gamenumber elif gameid: print gameid def fillCanvas(_, player, header): a = PysolStatsFormatter(_.app) writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H) if not a.writeStats(writer, player, header): writer.p('No entries for player ' + str(player) + '\n') destruct(writer) destruct(a) class CanvasWriter(PysolStatsFormatter.StringWriter): def __init__(_, canvas, font, h): _.canvas = canvas _.fg = canvas.cget('insertbackground') _.font = font _.h = h _.x = _.y = 0 _.gameid = None _.gamenumber = None _.canvas.config(yscrollincrement = h) def _addItem(_, id): _.canvas.dialog.nodes[id] = (_.gameid, _.gamenumber) def p(_, s): if _.y > 16000: return None (h1, h2) = (0, 0) while s and s[0] == '\n': s = s[1:] h1 = h1 + _.h while s and s[-1] == '\n': s = s[:-1] h2 = h2 + _.h _.y = _.y + h1 if s: id = _.canvas.create_text(_.x, _.y, text = s, anchor = 'nw', font = _.font, fill = _.fg) _._addItem(id) _.y = _.y + h2 def pheader(_, s): pass def pstats(_, t1, t2, t3, t4, t5, gameid = None): if _.y > 16000: return None _.gameid = gameid _.gamenumber = None (x, y) = (1, _.y) p = _._pstats_text h = 0 h = max(h, p(x, y, anchor = 'nw', text = str(t1))) h = max(h, p(x + 200, y, anchor = 'ne', text = str(t2))) h = max(h, p(x + 250, y, anchor = 'ne', text = str(t3))) h = max(h, p(x + 300, y, anchor = 'ne', text = str(t4))) h = max(h, p(x + 350, y, anchor = 'ne', text = str(t5))) _.pstats_perc(x + 372, y, str(t5)) _.y = _.y + h _.gameid = None def _pstats_text(_, x, y, **kw): kwdefault(kw, font = _.font, fill = _.fg) id = apply(_.canvas.create_text, (x, y), kw) _._addItem(id) return _.h def pstats_perc(_, x, y, t): if t: pass if not None if t[0] <= t[0] else t[0] <= '9': return None perc = int(round(string.atof(str(t)))) if perc < 1: return None (rx, ry, rw, rh) = (x, y + 1, 2 + 8 * 10, _.h - 5) if 1: w = int(round(rw * perc / 100.0)) if 1 and w < 1: return None if w > 0: w = max(3, w) w = min(rw - 2, w) id = _.canvas.create_rectangle(rx, ry, rx + w, ry + rh, width = 1, fill = '#00ff00', outline = '#000000') if w < rw: id = _.canvas.create_rectangle(rx + w, ry, rx + rw, ry + rh, width = 1, fill = '#ff0000', outline = '#000000') return None fill = None id = _.canvas.create_rectangle(rx, ry, rx + rw, ry + rh, width = 1, fill = fill, outline = '#808080') if 1: (rx, rw) = (rx + 1, rw - 1) (ry, rh) = (ry + 1, rh - 1) w = int(round(rw * perc / 100.0)) if w > 0: id = _.canvas.create_rectangle(rx, ry, rx + w, ry + rh, width = 0, fill = '#00ff00', outline = '') if w < rw: id = _.canvas.create_rectangle(rx + w, ry, rx + rw, ry + rh, width = 0, fill = '#ff0000', outline = '') return None p = 1.0 ix = rx + 2 for i in (1, 11, 21, 31, 41, 51, 61, 71, 81, 91): (r, g, b) = (255, 128 * p, 64 * p) c = '#%02x%02x%02x' % (int(r), int(g), int(b)) id = _.canvas.create_rectangle(ix, ry + 2, ix + 6, ry + rh - 2, width = 0, fill = c, outline = c) ix = ix + 8 p = max(0.0, p - 0.1) def plog(_, gamename, gamenumber, date, status, gameid = -1, won = -1): if gameid > 0: if gamenumber[0:1] <= gamenumber[0:1]: pass elif gamenumber[0:1] <= '9': _.gameid = gameid _.gamenumber = gamenumber _.p('%-25s %-20s %17s %s\n' % (gamename, gamenumber, date, status)) _.gameid = None _.gamenumber = None class FullLog_StatsDialog(AllGames_StatsDialog): YVIEW = 1 def fillCanvas(_, player, header): a = PysolStatsFormatter(_.app) writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H) if not a.writeFullLog(writer, player, header): writer.p('No log entries for ' + str(player) + '\n') destruct(a) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', ('Session log...', 104), ('Save to file', 203)), default = 0, font = getFont('canvas_fixed'), c_width = 76 * _.CHAR_W) return AllGames_StatsDialog.initKw(_, kw) class SessionLog_StatsDialog(FullLog_StatsDialog): def fillCanvas(_, player, header): a = PysolStatsFormatter(_.app) writer = _.CanvasWriter(_.canvas, _.font, _.CHAR_H) if not a.writeSessionLog(writer, player, header): writer.p('No current session log entries for ' + str(player) + '\n') destruct(a) def initKw(_, kw): kw = KwStruct(kw, strings = ('OK', ('Full log...', 103), ('Save to file', 204)), default = 0) return FullLog_StatsDialog.initKw(_, kw) class AbstractCard: def __init__(_, id, deck, suit, rank, game, x = 0, y = 0): _.id = id _.deck = deck _.suit = suit _.color = suit / 2 _.rank = rank _.x = x _.y = y _.item = None _.face_up = 0 _.hide_stack = None _.hide_x = _.hide_y = 0 def __str__(_): return 'Card(%d, %d, %d, %d)' % (_.id, _.deck, _.suit, _.rank) def isHidden(_): return _.hide_stack is not None def moveTo(_, x, y): _.moveBy((x - _.x) + _.hide_x, (y - _.y) + _.hide_y) def moveBy(_, dx, dy): (dx, dy) = (int(dx), int(dy)) if dx or dy: _.x = _.x + dx _.y = _.y + dy _.item.move(dx, dy) def tkraise(_, unhide = 1): if unhide: _.unhide() _.item.tkraise() def hide(_, stack): pass def unhide(_): pass def setSelected(_, s, group = None): pass def showFace(_, unhide = 1): raise SubclassResponsibility def showBack(_, unhide = 1): raise SubclassResponsibility def updateCardBackground(_, image): raise SubclassResponsibility class _HideableCard_1(AbstractCard): def hide(_, stack): if stack is _.hide_stack: return None if _.hide_stack: (hx, hy) = (stack.hide_x - _.hide_x, stack.hide_y - _.hide_y) else: (hx, hy) = (stack.hide_x, stack.hide_y) item = _.item item.canvas.tk.call(item.canvas._w, 'move', item.id, hx, hy) (_.hide_x, _.hide_y) = (stack.hide_x, stack.hide_y) _.hide_stack = stack def unhide(_): if _.hide_stack is None: return 0 item = _.item item.canvas.tk.call(item.canvas._w, 'move', item.id, -(_.hide_x), -(_.hide_y)) (_.hide_x, _.hide_y) = (0, 0) _.hide_stack = None return 1 class _HideableCard_2(AbstractCard): def hide(_, stack): if stack is _.hide_stack: return None _.item.config(state = 'hidden') _.hide_stack = stack def unhide(_): if _.hide_stack is None: return 0 _.item.config(state = 'normal') _.hide_stack = None return 1 _HideableCard = _HideableCard_1 if 1 and tkversion >= (8, 3, 0, 0): _HideableCard = _HideableCard_2 class _OneImageCard(_HideableCard): def __init__(_, id, deck, suit, rank, game, x = 0, y = 0): _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y) _._face_image = game.getCardFaceImage(deck, suit, rank) _._back_image = game.getCardBackImage(deck, suit, rank) _._active_image = _._back_image _.item = MfxCanvasImage(game.canvas, _.x, _.y, image = _._active_image, anchor = 'nw') def _setImage(_, image): if image is not _._active_image: _.item.config(image = image) _._active_image = image def showFace(_, unhide = 1): if not (_.face_up): _._setImage(image = _._face_image) _.tkraise(unhide) _.face_up = 1 def showBack(_, unhide = 1): if _.face_up: _._setImage(image = _._back_image) _.tkraise(unhide) _.face_up = 0 def updateCardBackground(_, image): _._back_image = image if not (_.face_up): _._setImage(image = image) def moveBy(_, dx, dy): (dx, dy) = (int(dx), int(dy)) _.x = _.x + dx _.y = _.y + dy item = _.item item.canvas.tk.call(item.canvas._w, 'move', item.id, dx, dy) class _OneImageCardWithHideByConfig(_OneImageCard): def hide(_, stack): if stack is _.hide_stack: return None _._setImage(image = None) _.hide_stack = stack def unhide(_): if _.hide_stack is None: return 0 if _.face_up: _._setImage(image = _._face_image) else: _._setImage(image = _._back_image) _.hide_stack = None return 1 def showFace(_, unhide = 1): if not (_.face_up): if unhide: _._setImage(image = _._face_image) _.item.tkraise() _.face_up = 1 def showBack(_, unhide = 1): if _.face_up: if unhide: _._setImage(image = _._back_image) _.item.tkraise() _.face_up = 0 def updateCardBackground(_, image): _._back_image = image if not (_.face_up) and not (_.hide_stack): _._setImage(image = image) class _TwoImageCard(_HideableCard): def __init__(_, id, deck, suit, rank, game, x = 0, y = 0): _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y) _.item = MfxCanvasGroup(game.canvas) _._TwoImageCard__face = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardFaceImage(deck, suit, rank), anchor = 'nw') _._TwoImageCard__back = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardBackImage(deck, suit, rank), anchor = 'nw') _._TwoImageCard__face.addtag(_.item) _._TwoImageCard__back.addtag(_.item) def showFace(_, unhide = 1): if not (_.face_up): if TK_DASH_PATCH: _._TwoImageCard__back.config(state = 'hidden') _._TwoImageCard__face.config(state = 'normal') _._TwoImageCard__face.tkraise() _.tkraise(unhide) _.face_up = 1 def showBack(_, unhide = 1): if _.face_up: if TK_DASH_PATCH: _._TwoImageCard__face.config(state = 'hidden') _._TwoImageCard__back.config(state = 'normal') _._TwoImageCard__back.tkraise() _.tkraise(unhide) _.face_up = 0 def updateCardBackground(_, image): _._TwoImageCard__back.config(image = image) class _TwoImageCardWithHideItem(_HideableCard): def __init__(_, id, deck, suit, rank, game, x = 0, y = 0): _HideableCard.__init__(_, id, deck, suit, rank, game, x = x, y = y) _.item = MfxCanvasGroup(game.canvas) _._TwoImageCardWithHideItem__face = MfxCanvasImage(game.canvas, _.x, _.y + 11000, image = game.getCardFaceImage(deck, suit, rank), anchor = 'nw') _._TwoImageCardWithHideItem__back = MfxCanvasImage(game.canvas, _.x, _.y, image = game.getCardBackImage(deck, suit, rank), anchor = 'nw') _._TwoImageCardWithHideItem__face.addtag(_.item) _._TwoImageCardWithHideItem__back.addtag(_.item) def showFace(_, unhide = 1): if not (_.face_up): _._TwoImageCardWithHideItem__back.move(0, 10000) _._TwoImageCardWithHideItem__face.move(0, -11000) _.tkraise(unhide) _.face_up = 1 def showBack(_, unhide = 1): if _.face_up: _._TwoImageCardWithHideItem__face.move(0, 11000) _._TwoImageCardWithHideItem__back.move(0, -10000) _.tkraise(unhide) _.face_up = 0 def updateCardBackground(_, image): _._TwoImageCardWithHideItem__back.config(image = image) Card = _TwoImageCardWithHideItem Card = _TwoImageCard Card = _OneImageCardWithHideByConfig Card = _OneImageCard class ImagesCardback: def __init__(_, index, name, image, menu_image = None): if menu_image is None: menu_image = image _.index = index _.name = name _.image = image _.menu_image = menu_image class Images: def __init__(_, dataloader, cs, r = 1): _.d = dataloader _.cs = cs _.reduced = r if cs is None: return None (_.CARDW, _.CARDH, _.CARDD) = (cs.CARDW / r, cs.CARDH / r, cs.CARDD / r) _.CARD_XOFFSET = 12 / r _.CARD_YOFFSET = cs.CARD_UP_YOFFSET if r > 1: _.CARD_YOFFSET = max(10, cs.CARD_UP_YOFFSET) / r (_.SHADOW_XOFFSET, _.SHADOW_YOFFSET) = (cs.SHADOW_XOFFSET / r, cs.SHADOW_YOFFSET / r) (_.CARD_DX, _.CARD_DY) = (cs.CARD_DX / r, cs.CARD_DY / r) _._shade_index = 0 _._card = [] _._back = [] _._bottom = [] _._letter = [] _._shadow = [] _._shade = [] def destruct(_): pass def __loadCard(_, filename, check_w = 1, check_h = 1): f = os.path.join(_.cs.dir, filename) img = loadImage(file = f) (w, h) = (img.width(), img.height()) if _.CARDW < 0: (_.CARDW, _.CARDH) = (w, h) elif check_w and w != _.CARDW and check_h and h != _.CARDH: raise Exception, 'Invalid size %dx%d of image %s' % (w, h, f) return img def __addBack(_, im1, name): r = max(_.CARDW / 40.0, _.CARDH / 60.0) r = max(2, int(round(r))) im2 = im1.subsample(r) _._back.append(ImagesCardback(len(_._back), name, im1, im2)) def _createMissingImages(_): if not (_._back): im = createImage(_.CARDW, _.CARDH, fill = '#a0a0a0', outline = '#000000') name = '' _._Images__addBack(im, name) _.cs.backnames = tuple(_.cs.backnames) + (name,) bottom = None while len(_._bottom) < 7: if bottom is None: bottom = createImage(_.CARDW, _.CARDH, fill = None, outline = '#000000') _._bottom.append(bottom) while len(_._letter) < 4: if bottom is None: bottom = createImage(_.CARDW, _.CARDH, fill = None, outline = '#000000') _._letter.append(bottom) def load(_, app, progress = None, fast = 0): ext = _.cs.ext[1:] pstep = 0 if progress: pstep = _.cs.ncards + len(_.cs.backnames) + _.cs.nbottoms + _.cs.nletters if not fast: pstep = pstep + _.cs.nshadows + 1 pstep = max(0, (80.0 - progress.percent) / pstep) for n in _.cs.getFaceCardNames(): _._card.append(_._Images__loadCard(n + _.cs.ext)) _._card[-1].filename = n if not __debug__ and len(_._card) == _.cs.ncards: raise AssertionError None if progress else _.cs.getFaceCardNames() for name in _.cs.backnames: try: if name: im = _._Images__loadCard(name) _._Images__addBack(im, name) except: 0 _.cs.backnames for i in range(_.cs.nbottoms): try: name = 'bottom%02d.%s' % (i + 1, ext) _._bottom.append(_._Images__loadCard(name)) except: 0 range(_.cs.nbottoms) None if progress else _.cs.backnames for rank in range(_.cs.nletters): try: name = 'l%02d.%s' % (rank + 1, ext) _._letter.append(_._Images__loadCard(name)) except: 0 range(_.cs.nletters) None if progress else _.cs.backnames if progress else range(_.cs.nbottoms) for i in range(_.cs.nshadows): if progress: progress.update(step = pstep) if fast: _._shade.append(None) else: _._shade.append(_._Images__loadCard('shade.' + ext)) if progress: progress.update(step = pstep) _._createMissingImages() return 1 def getFace(_, deck, suit, rank): index = suit * len(_.cs.ranks) + rank return _._card[index % _.cs.ncards] def getBack(_, deck, suit, rank): index = _.cs.backindex % len(_._back) return _._back[index].image def getTalonBottom(_): return _._bottom[0] def getReserveBottom(_): return _._bottom[0] def getSuitBottom(_, suit = -1): if not __debug__ and type(suit) is types.IntType: raise AssertionError if suit == -1: return _._bottom[1] i = 3 + suit if i >= len(_._bottom): return _._bottom[1] return _._bottom[i] def getBraidBottom(_): return _._bottom[2] def getLetter(_, rank): if __debug__: if rank <= rank: pass elif not rank <= 3: raise AssertionError if rank >= len(_._letter): return _._bottom[0] return _._letter[rank] def getShadow(_, ncards): if not __debug__ and ncards >= 0: raise AssertionError if ncards >= len(_._shadow): return None return _._shadow[ncards] def getShade(_): return _._shade[_._shade_index] def getCardbacks(_): return _._back class SubsampledImages(Images): def __init__(_, images, r = 2): Images.__init__(_, None, images.cs, r = r) _._card = _._subsample(images._card, r) _._bottom = _._subsample(images._bottom, r) _._letter = _._subsample(images._letter, r) for _back in images._back: pass (CW, CH) = (_.CARDW, _.CARDH) for im in images._shade: pass def getShadow(_, ncards): return None def _subsample(_, l, r): s = [] for im in l: pass return s class AtomicMove: def do(_, game): _.redo(game) def __repr__(_): return str(_.__dict__) def __str__(_): return str(_.__dict__) def cmpForRedo(_, other): return -1 class AMoveMove(AtomicMove): def __init__(_, ncards, from_stack, to_stack, frames, shadow = -1): if not __debug__ and from_stack is not to_stack: raise AssertionError _.ncards = ncards _.from_stack_id = from_stack.id _.to_stack_id = to_stack.id _.frames = frames _.shadow = shadow def __doMove(_, game, ncards, from_stack, to_stack): if game.moves.state == game.S_PLAY: if not __debug__ and to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]): raise AssertionError cards = [] for i in range(ncards): card = from_stack.removeCard() cards.append(card) cards.reverse() for c in cards: to_stack.addCard(c) def redo(_, game): _._AMoveMove__doMove(game, _.ncards, game.allstacks[_.from_stack_id], game.allstacks[_.to_stack_id]) def undo(_, game): _._AMoveMove__doMove(game, _.ncards, game.allstacks[_.to_stack_id], game.allstacks[_.from_stack_id]) def cmpForRedo(_, other): if not cmp(_.ncards, other.ncards) and cmp(_.from_stack_id, other.from_stack_id): pass return cmp(_.to_stack_id, other.to_stack_id) class AFlipMove(AtomicMove): def __init__(_, stack): _.stack_id = stack.id def __doMove(_, game, stack): card = stack.cards[-1] if card.face_up: card.showBack() else: card.showFace() def redo(_, game): _._AFlipMove__doMove(game, game.allstacks[_.stack_id]) def undo(_, game): _._AFlipMove__doMove(game, game.allstacks[_.stack_id]) def cmpForRedo(_, other): return cmp(_.stack_id, other.stack_id) class ATurnStackMove(AtomicMove): def __init__(_, from_stack, to_stack, update_flags = 1): if not __debug__ and from_stack is not to_stack: raise AssertionError _.from_stack_id = from_stack.id _.to_stack_id = to_stack.id _.update_flags = update_flags def redo(_, game): from_stack = game.allstacks[_.from_stack_id] to_stack = game.allstacks[_.to_stack_id] if not __debug__ and len(from_stack.cards) > 0: raise AssertionError if not __debug__ and len(to_stack.cards) == 0: raise AssertionError l = len(from_stack.cards) for i in range(l): unhide = 1 card = from_stack.removeCard(unhide = unhide, update = 0) if not __debug__ and card.face_up: raise AssertionError 0 to_stack.addCard(card, unhide = unhide, update = 0) card.showBack(unhide = unhide) from_stack.updateText() to_stack.updateText() def undo(_, game): from_stack = game.allstacks[_.to_stack_id] to_stack = game.allstacks[_.from_stack_id] if not __debug__ and len(from_stack.cards) > 0: raise AssertionError if not __debug__ and len(to_stack.cards) == 0: raise AssertionError l = len(from_stack.cards) for i in range(l): unhide = 1 card = from_stack.removeCard(unhide = unhide, update = 0) if not __debug__ and not (card.face_up): raise AssertionError 0 card.showFace(unhide = unhide) to_stack.addCard(card, unhide = unhide, update = 0) from_stack.updateText() to_stack.updateText() def cmpForRedo(_, other): if not cmp(_.from_stack_id, other.from_stack_id) and cmp(_.to_stack_id, other.to_stack_id): pass return cmp(_.update_flags, other.update_flags) class NEW_ATurnStackMove(AtomicMove): def __init__(_, from_stack, to_stack, update_flags = 1): if not __debug__ and from_stack is not to_stack: raise AssertionError _.from_stack_id = from_stack.id _.to_stack_id = to_stack.id _.update_flags = update_flags def __doMove(_, from_stack, to_stack, show_face): if not __debug__ and len(from_stack.cards) > 0: raise AssertionError if not __debug__ and len(to_stack.cards) == 0: raise AssertionError for card in from_stack.cards: card.item.dtag(from_stack.group) card.item.addtag(to_stack.group) if show_face: if not __debug__ and not (card.face_up): raise AssertionError 0 card.showFace(unhide = 0) elif not __debug__ and card.face_up: raise AssertionError card.showBack(unhide = 0) to_stack.cards = from_stack.cards from_stack.cards = [] from_stack.refreshView() from_stack.updateText() to_stack.refreshView() to_stack.updateText() def redo(_, game): from_stack = game.allstacks[_.from_stack_id] to_stack = game.allstacks[_.to_stack_id] if _.update_flags & 1: if not __debug__ and to_stack is game.s.talon: raise AssertionError if __debug__: if not to_stack.round < to_stack.max_rounds or to_stack.max_rounds < 0: raise AssertionError to_stack.round = to_stack.round + 1 _._NEW_ATurnStackMove__doMove(from_stack, to_stack, 0) def undo(_, game): from_stack = game.allstacks[_.from_stack_id] to_stack = game.allstacks[_.to_stack_id] if _.update_flags & 1: if not __debug__ and to_stack is game.s.talon: raise AssertionError if not __debug__ and to_stack.round > 1: raise AssertionError to_stack.round = to_stack.round - 1 _._NEW_ATurnStackMove__doMove(to_stack, from_stack, 1) def cmpForRedo(_, other): if not cmp(_.from_stack_id, other.from_stack_id) and cmp(_.to_stack_id, other.to_stack_id): pass return cmp(_.update_flags, other.update_flags) class AUpdateStackMove(AtomicMove): def __init__(_, stack, flags): _.stack_id = stack.id _.flags = flags def __doMove(_, game, stack, undo): if _.flags & 64: stack.updateModel(undo, _.flags) elif _.flags & 16: stack.updateText() if _.flags & 32: stack.refreshView() def redo(_, game): if _.flags & 3 in (1, 3): _._AUpdateStackMove__doMove(game, game.allstacks[_.stack_id], 0) def undo(_, game): if _.flags & 3 in (2, 3): _._AUpdateStackMove__doMove(game, game.allstacks[_.stack_id], 1) def cmpForRedo(_, other): if not cmp(_.stack_id, other.stack_id): pass return cmp(_.flags, other.flags) AUpdateStackModelMove = AUpdateStackMove AUpdateStackViewMove = AUpdateStackMove class ANextRoundMove(AtomicMove): def __init__(_, stack): _.stack_id = stack.id def redo(_, game): stack = game.allstacks[_.stack_id] if not __debug__ and stack is game.s.talon: raise AssertionError if __debug__: if not stack.round < stack.max_rounds or stack.max_rounds < 0: raise AssertionError stack.round = stack.round + 1 stack.updateText() def undo(_, game): stack = game.allstacks[_.stack_id] if not __debug__ and stack is game.s.talon: raise AssertionError if not __debug__ and stack.round > 1: raise AssertionError stack.round = stack.round - 1 stack.updateText() def cmpForRedo(_, other): return cmp(_.stack_id, other.stack_id) class ASaveSeedMove(AtomicMove): def __init__(_, game): _.seed = game.random.getSeed() def redo(_, game): game.random.setSeed(_.seed) def undo(_, game): game.random.setSeed(_.seed) def cmpForRedo(_, other): return cmp(_.seed, other.seed) class AShuffleStackMove(AtomicMove): def __init__(_, stack, game): _.stack_id = stack.id _.card_ids = tuple(map((lambda c: c.id), stack.cards)) _.seed = game.random.getSeed() def redo(_, game): stack = game.allstacks[_.stack_id] if not __debug__ and stack is game.s.talon: raise AssertionError if not __debug__ and _.card_ids == tuple(map((lambda c: c.id), stack.cards)): raise AssertionError game.random.setSeed(_.seed) seq = stack.cards n = len(seq) - 1 while n > 0: j = game.random.randint(0, n) (seq[n], seq[j]) = (seq[j], seq[n]) n = n - 1 stack.refreshView() def undo(_, game): stack = game.allstacks[_.stack_id] cards = [] for id in _.card_ids: c = game.cards[id] if not __debug__ and c.id == id: raise AssertionError 0 cards.append(c) stack.cards = cards game.random.setSeed(_.seed) stack.refreshView() def cmpForRedo(_, other): if not cmp(_.stack_id, other.stack_id) and cmp(_.card_ids, other.card_ids): pass return cmp(_.seed, other.seed) def cardsFaceUp(cards): if not cards: return 0 for c in cards: pass return 1 def cardsFaceDown(cards): if not cards: return 0 for c in cards: pass return 1 def isRankSequence(cards, mod = 8192, dir = -1): if not cardsFaceUp(cards): return 0 c1 = cards[0] for c2 in cards[1:]: c1 = c2 return 1 def isAlternateColorSequence(cards, mod = 8192, dir = -1): if not cardsFaceUp(cards): return 0 c1 = cards[0] for c2 in cards[1:]: c1 = c2 return 1 def isSameColorSequence(cards, mod = 8192, dir = -1): if not cardsFaceUp(cards): return 0 c1 = cards[0] for c2 in cards[1:]: c1 = c2 return 1 def isSameSuitSequence(cards, mod = 8192, dir = -1): if not cardsFaceUp(cards): return 0 c1 = cards[0] for c2 in cards[1:]: c1 = c2 return 1 def isAnySuitButOwnSequence(cards, mod = 8192, dir = -1): if not cardsFaceUp(cards): return 0 c1 = cards[0] for c2 in cards[1:]: c1 = c2 return 1 def getNumberOfFreeStacks(stacks): return len(filter((lambda s: not (s.cards)), stacks)) def getPileFromStacks(stacks, reverse = 0): cards = [] for s in stacks: cards.append(s.cards[-1]) if reverse: cards.reverse() return cards class Stack: def __init__(_, x, y, game, cap = { }): id = len(game.allstacks) game.allstacks.append(_) x = int(round(x)) y = int(round(y)) mapkey = (x, y) game.stackmap[mapkey] = id (model, view, controller) = (_, _, _) model.id = id model.game = game model.cards = [] model.cap = Struct(suit = -1, color = -1, rank = -1, base_suit = -1, base_color = -1, base_rank = -1, dir = 0, mod = 8192, max_move = 0, max_accept = 0, max_cards = 999999, min_move = 1, min_accept = 1, min_cards = 0) model.cap.update(cap) if not __debug__ and type(model.cap.suit) is types.IntType: raise AssertionError if not __debug__ and type(model.cap.color) is types.IntType: raise AssertionError if not __debug__ and type(model.cap.rank) is types.IntType: raise AssertionError if not __debug__ and type(model.cap.base_suit) is types.IntType: raise AssertionError if not __debug__ and type(model.cap.base_color) is types.IntType: raise AssertionError if not __debug__ and type(model.cap.base_rank) is types.IntType: raise AssertionError view.x = x view.y = y view.canvas = game.canvas view.CARD_XOFFSET = 0 view.CARD_YOFFSET = 0 view.group = MfxCanvasGroup(view.canvas) view.images = Struct(bottom = None, redeal = None, redeal_img = None) view.items = Struct(bottom = None) view.texts = Struct(ncards = None, rounds = None, redeal = None, redeal_str = None, misc = None) view.top_bottom = None if view.x >= -100: pass view.is_visible = view.y >= -100 view.is_open = -1 view.can_hide_cards = -1 view.max_shadow_cards = -1 def destruct(_): unbind_destroy(_.group) def prepareStack(_): _.prepareView() if _.is_visible: _.initBindings() def initBindings(_): group = _.group bind(group, '<1>', _._Stack__clickEventHandler) bind(group, '<B1-Motion>', _._Stack__motionEventHandler) bind(group, '<ButtonRelease-1>', _._Stack__releaseEventHandler) bind(group, '<Control-1>', _._Stack__controlclickEventHandler) bind(group, '<Shift-1>', _._Stack__shiftclickEventHandler) bind(group, '<Double-1>', _._Stack__doubleclickEventHandler) bind(group, '<3>', _._Stack__rightclickEventHandler) bind(group, '<2>', _._Stack__middleclickEventHandler) bind(group, '<Control-3>', _._Stack__middleclickEventHandler) def prepareView(_): (ox, oy) = (_.CARD_XOFFSET, _.CARD_YOFFSET) if type(ox) is types.IntType: _.CARD_XOFFSET = (ox,) else: _.CARD_XOFFSET = tuple(map(int, map(round, ox))) if type(oy) is types.IntType: _.CARD_YOFFSET = (oy,) else: _.CARD_YOFFSET = tuple(map(int, map(round, oy))) if _.can_hide_cards < 0: _.can_hide_cards = _.is_visible if _.cap.max_cards < 3: _.can_hide_cards = 0 elif filter(None, _.CARD_XOFFSET): _.can_hide_cards = 0 elif filter(None, _.CARD_YOFFSET): _.can_hide_cards = 0 elif _.canvas.preview: _.can_hide_cards = 0 if _.can_hide_cards: (CW, CH) = (_.game.app.images.CARDW, _.game.app.images.CARDH) cx = _.x + CW / 2 cy = _.y + CH / 2 if cy < 3 * CH / 2: (_.hide_x, _.hide_y) = (0, -10000) elif cx < 3 * CW / 2: (_.hide_x, _.hide_y) = (-10000, 0) elif cy > _.game.height - 3 * CH / 2: (_.hide_x, _.hide_y) = (0, 10000) else: (_.hide_x, _.hide_y) = (10000, 0) if _.is_open < 0: if not _.is_visible and abs(_.CARD_XOFFSET[0]) >= 5: pass _.is_open = abs(_.CARD_YOFFSET[0]) >= 5 if _.max_shadow_cards < 0: _.max_shadow_cards = 999999 if abs(_.CARD_YOFFSET[0]) != _.game.app.images.CARD_YOFFSET: _.max_shadow_cards = 1 if _.is_visible: _.prepareBottom() def prepareBottom(_): if __debug__: if not _.is_visible and _.images.bottom is None: raise AssertionError img = _.getBottomImage() if img is not None: _.images.bottom = MfxCanvasImage(_.canvas, _.x, _.y, image = img, anchor = ANCHOR_NW) _.images.bottom.addtag(_.group) _.top_bottom = _.images.bottom def prepareInvisibleBottom(_): if __debug__: if not _.is_visible and _.items.bottom is None: raise AssertionError images = _.game.app.images _.items.bottom = MfxCanvasRectangle(_.canvas, _.x, _.y, _.x + images.CARDW, _.y + images.CARDH, fill = '', outline = '', width = 0) _.items.bottom.addtag(_.group) _.top_bottom = _.items.bottom def assertStack(_): if not __debug__ and _.cap.min_move > 0: raise AssertionError if not __debug__ and _.cap.min_accept > 0: raise AssertionError if not __debug__ and not hasattr(_, 'suit'): raise AssertionError def addCard(_, card, unhide = 1, update = 1): (model, view) = (_, _) model.cards.append(card) card.tkraise(unhide = unhide) if view.can_hide_cards and len(model.cards) >= 3: model.cards[-3].hide(_) card.item.addtag(view.group) view._position(card) if update: view.updateText() return card def removeCard(_, card = None, unhide = 1, update = 1): (model, view) = (_, _) if not __debug__ and len(model.cards) > 0: raise AssertionError if card is None: card = model.cards[-1] card.item.dtag(view.group) if unhide and _.can_hide_cards: card.unhide() if len(_.cards) >= 3: model.cards[-3].unhide() del model.cards[-1] else: card.item.dtag(view.group) if unhide and view.can_hide_cards: card.unhide() if len(model.cards) >= 3: if card is model.cards[-1] or model is _.cards[-2]: model.cards[-3].unhide() model.cards.remove(card) if update: view.updateText() return card def getCard(_): if _.cards: return _.cards[-1] return None def getPile(_): if _.cap.max_move > 0: cards = _.cards[-(_.cap.max_move):] while len(cards) >= _.cap.min_move: if _.canMoveCards(cards): return cards del cards[0] return None def _position(_, card): (x, y) = _.getPositionFor(card) card.moveTo(x, y) def _findCard(_, event): (model, view) = (_, _) if event is not None and model.cards: return view.canvas.findCard(_, event) return -1 def _findCardXY(_, x, y, cards = None): (model, view) = (_, _) if cards is None: cards = model.cards images = _.game.app.images index = -1 for i in range(len(cards)): c = cards[i] r = (c.x, c.y, c.x + images.CARDW, c.y + images.CARDH) return index def updateModel(_, undo, flags): pass def copyModel(_, clone): clone.id = _.id clone.game = _.game clone.cap = _.cap def getRankDir(_, cards = None): if cards is None: cards = _.cards[-2:] if len(cards) < 2: return 0 dir = (cards[-1].rank - cards[-2].rank) % _.cap.mod if dir > _.cap.mod / 2: return dir - _.cap.mod return dir def basicIsBlocked(_): return 0 def basicAcceptsCards(_, from_stack, cards): if from_stack is _ or _.basicIsBlocked(): return 0 cap = _.cap l = len(cards) if l < cap.min_accept or l > cap.max_accept: return 0 l = l + len(_.cards) if l > cap.max_cards: return 0 for c in cards: if cap.suit >= 0 and c.suit != cap.suit: return 0 if cap.color >= 0 and c.color != cap.color: return 0 if cap.rank >= 0 and c.rank != cap.rank: return 0 if _.cards: return _.cards[-1].face_up else: c = cards[0] if cap.base_suit >= 0 and c.suit != cap.base_suit: return 0 if cap.base_color >= 0 and c.color != cap.base_color: return 0 if cap.base_rank >= 0 and c.rank != cap.base_rank: return 0 return 1 def basicCanMoveCards(_, cards): if _.basicIsBlocked(): return 0 cap = _.cap l = len(cards) if l < cap.min_move or l > cap.max_move: return 0 l = len(_.cards) - l if l < cap.min_cards: return 0 return cardsFaceUp(cards) def acceptsCards(_, from_stack, cards): return 0 def canMoveCards(_, cards): return 0 def canFlipCard(_): return 0 def canDropCards(_, stacks): return (None, 0) def resetGame(_): pass def __repr__(_): return '%s(%d)' % (_.__class__.__name__, _.id) def flipMove(_): _.game.flipMove(_) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow) _.fillStack() def fillStack(_): _.game.fillStack(_) def playFlipMove(_, sound = 1): if sound: _.game.playSample('flip', 5) _.flipMove() if not _.game.checkForWin(): _.game.autoPlay() _.game.finishMove() def playMoveMove(_, ncards, to_stack, frames = -1, shadow = -1, sound = 1): if sound: if to_stack in _.game.s.foundations: _.game.playSample('drop', priority = 30) else: _.game.playSample('move', priority = 10) _.moveMove(ncards, to_stack, frames = frames, shadow = shadow) if not _.game.checkForWin(): if not (_ in _.game.s.foundations): _.game.autoPlay() _.game.finishMove() def getBottomImage(_): return None def getPositionFor(_, card): (model, view) = (_, _) if view.can_hide_cards: return (view.x, view.y) (x, y) = (view.x, view.y) (ix, iy, lx, ly) = (0, 0, len(view.CARD_XOFFSET), len(view.CARD_YOFFSET)) for c in model.cards: x = x + view.CARD_XOFFSET[ix] y = y + view.CARD_YOFFSET[iy] ix = (ix + 1) % lx iy = (iy + 1) % ly return (x, y) def refreshView(_): (model, view) = (_, _) cards = model.cards if not (view.is_visible) or len(cards) < 2: return None item = cards[0].item (x, y) = (view.x, view.y) (ix, iy, lx, ly) = (0, 0, len(view.CARD_XOFFSET), len(view.CARD_YOFFSET)) for c in cards[1:]: c.item.tkraise(item) item = c.item def updateText(_): if _.game.preview > 1 or _.texts.ncards is None: return None t = '' format = '%d' if _.texts.ncards.text_format is not None: format = _.texts.ncards.text_format if format == '%D': format = '' if _.cards: format = '%d' if format: t = format % len(_.cards) if 0 and _.game.app.debug: visible = 0 for c in _.cards: if c.isHidden(): if not __debug__ and c.hide_stack is not None: raise AssertionError 0 if __debug__: if not c.hide_x != 0 or c.hide_y != 0: raise AssertionError _.cards else: visible = visible + 1 if not __debug__ and c.hide_stack is None: raise AssertionError 0 if __debug__: if not c.hide_x == 0 and c.hide_y == 0: raise AssertionError _.cards t = t + ' %2d' % visible _.texts.ncards.config(text = t) def basicShallHighlightSameRank(_, card): if not __debug__ and card in _.cards: raise AssertionError if not (_.is_visible) or not (card.face_up): return 0 if card is _.cards[-1]: return 1 return _.is_open def basicShallHighlightMatch(_, card): return _.basicShallHighlightSameRank(card) def highlightSameRank(_, event): i = _._findCard(event) if i < 0: return 0 card = _.cards[i] if not _.basicShallHighlightSameRank(card): return 0 col = _.game.app.opt.highlight_samerank_colors info = [ (_, card, card, col[1])] for s in _.game.allstacks: for c in s.cards: if s.basicShallHighlightSameRank(c): info.append((s, c, c, col[3])) _.game.stats.highlight_samerank = _.game.stats.highlight_samerank + 1 return _.game._highlightCards(info, _.game.app.opt.highlight_samerank_sleep) def highlightMatchingCards(_, event): i = _._findCard(event) if i < 0: return 0 card = _.cards[i] if not _.basicShallHighlightMatch(card): return 0 col = _.game.app.opt.highlight_cards_colors c1 = c2 = card info = [] found = 0 for s in _.game.allstacks: for c in s.cards: if not s.basicShallHighlightMatch(c): continue if _.game.shallHighlightMatch(_, card, s, c): found = 1 if s is _: j = _.cards.index(c) if i - 1 == j: c1 = c continue if i + 1 == j: c2 = c continue info.append((s, c, c, col[3])) if found: if info: _.game.stats.highlight_cards = _.game.stats.highlight_cards + 1 info.append((_, c1, c2, col[1])) return _.game._highlightCards(info, _.game.app.opt.highlight_cards_sleep) return 0 def clickHandler(_, event): return 0 def middleclickHandler(_, event): if not (_.is_open): return 0 i = _._findCard(event) positions = len(_.cards) - i - 1 if i < 0 and positions <= 0 or not (_.cards[i].face_up): return 0 _.cards[i].item.tkraise() _.game.canvas.update_idletasks() _.game.sleep(_.game.app.opt.raise_card_sleep) _.cards[i].item.lower(_.cards[i + 1].item) _.game.canvas.update_idletasks() return 1 def rightclickHandler(_, event): return 0 def doubleclickHandler(_, event): return _.clickHandler(event) def controlclickHandler(_, event): return 0 def shiftclickHandler(_, event): if _.game.app.opt.highlight_samerank: return _.highlightSameRank(event) return 0 def shiftrightclickHandler(_, event): return 0 def releaseHandler(_, event, drag, sound = 1): if drag.cards: if sound: _.game.playSample('nomove') _.moveCardsBackHandler(event, drag) def moveCardsBackHandler(_, event, drag): for card in drag.cards: _._position(card) def __defaultClickEventHandler(_, event, handler, start_drag = 0): if _.game.demo: _.game.stopDemo(event) if _.game.busy: return EVENT_HANDLED if _.game.drag.stack: _.game.drag.stack.cancelDrag(event) if start_drag: r = handler(event) if r <= 0: sound = r == 0 _.startDrag(event, sound = sound) else: handler(event) return EVENT_HANDLED def __clickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.clickHandler, 1) def __doubleclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.doubleclickHandler, 1) def __middleclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.middleclickHandler) def __rightclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.rightclickHandler) def __controlclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.controlclickHandler) def __shiftclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.shiftclickHandler) def __shiftrightclickEventHandler(_, event): return _._Stack__defaultClickEventHandler(event, _.shiftrightclickHandler) def __motionEventHandler(_, event): if _.game.demo: _.game.stopDemo(event) if _.game.busy: return EVENT_HANDLED if 1: drag = _.game.drag if drag.timer is None: drag.timer = after_idle(_.canvas, _.keepDragTimer) drag.event = event else: _.keepDrag(event) return EVENT_HANDLED def __releaseEventHandler(_, event): if _.game.demo: _.game.stopDemo(event) if _.game.busy: return EVENT_HANDLED _.keepDrag(event) _.finishDrag(event) return EVENT_HANDLED def __enterEventHandler(_, event): _._Stack__leaveEventHandler(None) opt = _.game.app.opt if opt.magnetic_mouse: t = int(opt.magnetic_mouse_time * 1000.0) _.magnetic_mouse_timer = after(_.canvas, t, _._Stack__magneticMouseTimerHandler) return EVENT_HANDLED def __leaveEventHandler(_, event): after_cancel(_.magnetic_mouse.timer) _.magnetic_mouse.timer = None _.magnetic_mouse.event = None return EVENT_HANDLED def __magneticMouseTimerHandler(_): event = _.magnetic_mouse.event _._Stack__leaveEventHandler(None) if event and not (_.game.drag.stack): pass def startDrag(_, event, sound = 1): if not __debug__ and _.game.drag.stack is None: raise AssertionError i = _._findCard(event) if i < 0 or not _.canMoveCards(_.cards[i:]): return None if sound: _.game.playSample('startdrag') _.lastx = event.x _.lasty = event.y game = _.game drag = game.drag drag.start_x = event.x drag.start_y = event.y drag.stack = _ drag.noshade_stacks = [ _] drag.cards = _.cards[i:] images = game.app.images drag.shadows = _.createShadows(drag.cards) for s in drag.shadows: s.tkraise() (sx, sy) = (-(images.SHADOW_XOFFSET), -(images.SHADOW_YOFFSET)) for card in drag.cards: card.tkraise() card.moveBy(sx, sy) def keepDrag(_, event): drag = _.game.drag if not (drag.cards): return None if not __debug__ and _ is drag.stack: raise AssertionError dx = event.x - _.lastx dy = event.y - _.lasty drag.event = None def keepDragTimer(_): drag = _.game.drag after_cancel(drag.timer) drag.timer = None if drag.event: _.keepDrag(drag.event) def createShadows(_, cards, dx = 0, dy = 0): if not (_.game.app.opt.shadow) or _.canvas.preview > 1: return () l = len(cards) if l == 0 or l > _.max_shadow_cards: return () images = _.game.app.images (cx, cy) = (cards[0].x, cards[0].y) for c in cards[1:]: cy = c.y (img0, img1) = (images.getShadow(0), images.getShadow(l)) return () def _deleteShade(_): if _.game.drag.shade_img: _.game.drag.shade_img.delete() _.game.drag.shade_img = None _.game.drag.shade_stack = None def _updateShade(_): game = _.game images = game.app.images img = images.getShade() if img is None: return None (CW, CH) = (images.CARDW, images.CARDH) drag = game.drag c = drag.cards[0] stacks = (game.getClosestStack(c, drag.stack),) (r1_0, r1_1, r1_2, r1_3) = (c.x, c.y, c.x + CW, c.y + CH) (sstack, sdiff, sx, sy) = (None, 999999999, 0, 0) for s in stacks: if s.cards: c = s.cards[-1] r2 = (c.x, c.y, c.x + CW, c.y + CH) else: r2 = (s.x, s.y, s.x + CW, s.y + CH) if r1_2 <= r2[0] and r1_3 <= r2[1] and r2[2] <= r1_0 or r2[3] <= r1_1: continue if s in drag.canshade_stacks: pass elif s.acceptsCards(drag.stack, drag.cards): drag.canshade_stacks.append(s) else: drag.noshade_stacks.append(s) diff = (r1_0 - r2[0]) ** 2 + (r1_1 - r2[1]) ** 2 if diff < sdiff: (sstack, sdiff, sx, sy) = (s, diff, r2[0], r2[1]) if sstack is drag.shade_stack: return None if sstack is None: _._deleteShade() return None drag.shade_stack = sstack if drag.shade_img: drag.shade_img.moveTo(sx, sy) else: img = MfxCanvasImage(game.canvas, sx, sy, image = img, anchor = ANCHOR_NW) drag.shade_img = img if drag.shadows: img.lower(drag.shadows[0]) else: img.lower(drag.cards[0].item) def _stopDrag(_): drag = _.game.drag after_cancel(drag.timer) drag.timer = None _._deleteShade() drag.canshade_stacks = [] drag.noshade_stacks = [] for s in drag.shadows: s.delete() drag.shadows = [] drag.stack = None drag.cards = [] def finishDrag(_, event = None): if _.game.app.opt.dragcursor: _.game.canvas.config(cursor = _.game.app.top_cursor) drag = _.game.drag.copy() _._stopDrag() if drag.cards: if not __debug__ and drag.stack is _: raise AssertionError _.releaseHandler(event, drag) def cancelDrag(_, event = None): if _.game.app.opt.dragcursor: _.game.canvas.config(cursor = _.game.app.top_cursor) drag = _.game.drag.copy() _._stopDrag() if drag.cards: if not __debug__ and drag.stack is _: raise AssertionError _.moveCardsBackHandler(event, drag) class DealRow_StackMethods: def dealRow(_, rows = None, flip = 1, reverse = 0, frames = -1, sound = 0): if rows is None: rows = _.game.s.rows if sound and frames and _.game.app.opt.animations: _.game.startDealSample() n = _.dealToStacks(rows, flip, reverse, frames) if sound: _.game.stopSamples() return n def dealRowAvail(_, rows = None, flip = 1, reverse = 0, frames = -1, sound = 0): if rows is None: rows = _.game.s.rows if sound and frames and _.game.app.opt.animations: _.game.startDealSample() if len(_.cards) < len(rows): rows = rows[:len(_.cards)] n = _.dealToStacks(rows, flip, reverse, frames) if sound: _.game.stopSamples() return n def dealToStacks(_, stacks, flip = 1, reverse = 0, frames = -1): if not (_.cards) or not stacks: return 0 if not __debug__ and len(_.cards) >= len(stacks): raise AssertionError old_state = _.game.enterState(_.game.S_DEAL) if reverse: stacks = list(stacks)[:] stacks.reverse() for r in stacks: if not __debug__ and not (_.getCard().face_up): raise AssertionError 0 if not __debug__ and r is not _: raise AssertionError stacks if frames == 0 and _.game.moves.state == _.game.S_INIT: c = _.removeCard(update = 0) r.addCard(c, update = 0) if flip: c.showFace() elif flip: _.game.flipMove(_) _.game.moveMove(1, _, r, frames = frames) _.game.leaveState(old_state) return len(stacks) def dealToStacksOrFoundations(_, stacks, flip = 1, reverse = 0, frames = -1, rank = -1): if rank < 0: rank = _.game.s.foundations[0].cap.base_rank if not (_.cards) or not stacks: return 0 old_state = _.game.enterState(_.game.S_DEAL) if reverse: stacks = list(stacks)[:] stacks.reverse() n = 0 for r in stacks: if not __debug__ and r is not _: raise AssertionError 0 while _.cards: n = n + 1 if flip: _.game.flipMove(_) if flip and _.cards[-1].rank == rank: for s in _.game.s.foundations: if not __debug__ and s is not _: raise AssertionError 0 if s.acceptsCards(_, _.cards[-1:]): _.game.moveMove(1, _, s, frames = frames) break else: _.game.moveMove(1, _, r, frames = frames) break _.game.leaveState(old_state) return n class DealBaseCard_StackMethods: def dealSingleBaseCard(_, frames = -1, update_saveinfo = 1): c = _.cards[-1] _.dealBaseCards(ncards = 1, frames = frames, update_saveinfo = 0) for s in _.game.s.foundations: s.cap.base_rank = c.rank return c def dealBaseCards(_, ncards = 1, frames = -1, update_saveinfo = 1): if not __debug__ and _.game.moves.state == _.game.S_INIT: raise AssertionError if not __debug__ and not (_.base_cards): raise AssertionError while ncards > 0: if not __debug__ and _.cards: raise AssertionError c = _.cards[-1] for s in _.game.s.foundations: pass elif not __debug__ and 0: raise AssertionError None if not (s.cards) and s.cap.base_suit < 0 or s.cap.base_suit == c.suit else _.game.s.foundations s = None s.cap.base_rank = c.rank if update_saveinfo: cap = Struct(base_rank = c.rank) _.game.saveinfo.stack_caps.append((s.id, cap)) if not (c.face_up): _.game.flipMove(_) _.game.moveMove(1, _, s, frames = frames) ncards = ncards - 1 class TalonStack(Stack, DealRow_StackMethods, DealBaseCard_StackMethods): def __init__(_, x, y, game, max_rounds = 1, num_deal = 1, **cap): Stack.__init__(_, x, y, game, cap = cap) _.max_rounds = max_rounds _.num_deal = num_deal _.resetGame() def resetGame(_): _.round = 1 _.base_cards = [] def assertStack(_): Stack.assertStack(_) n = _.game.gameinfo.redeals if n < 0: if not __debug__ and _.max_rounds == n: raise AssertionError elif not __debug__ and _.max_rounds == n + 1: raise AssertionError def clickHandler(_, event): return _.game.dealCards(sound = 1) def rightclickHandler(_, event): return _.clickHandler(event) def canDealCards(_): return len(_.cards) > 0 def dealCards(_, sound = 0): pass def removeAllCards(_): for stack in _.game.allstacks: while stack.cards: stack.removeCard(unhide = 0, update = 0) continue 0 for stack in _.game.allstacks: stack.updateText() def updateText(_, update_rounds = 1, update_redeal = 1): Stack.updateText(_) if update_rounds and _.game.preview <= 1: if _.texts.rounds is not None: t = 'Round %d' % _.round _.texts.rounds.config(text = t) if update_redeal: deal = _.canDealCards() != 0 if _.images.redeal is not None: img = _.getRedealImages()[deal] if img is not None and img is not _.images.redeal_img: _.images.redeal.config(image = img) _.images.redeal_img = img t = ('', 'Redeal')[deal] else: t = ('Stop', 'Redeal')[deal] if _.texts.redeal is not None and _.game.preview <= 1: if t != _.texts.redeal_str: _.texts.redeal.config(text = t) _.texts.redeal_str = t def prepareView(_): Stack.prepareView(_) if not (_.is_visible) or _.images.bottom is None: return None if _.images.redeal is not None or _.texts.redeal is not None: return None if _.game.preview > 1: return None images = _.game.app.images (cx, cy, ca) = (_.x + images.CARDW / 2, _.y + images.CARDH / 2, 'center') if images.CARDW >= 54 and images.CARDH >= 54: img = _.getRedealImages()[_.max_rounds != 1] if img is not None: _.images.redeal = MfxCanvasImage(_.game.canvas, cx, cy, image = img, anchor = 'center') _.images.redeal_img = img _.images.redeal.tkraise(_.top_bottom) _.images.redeal.addtag(_.group) _.top_bottom = _.images.redeal if images.CARDH >= 90: (cy, ca) = (_.y + images.CARDH - 4, 's') else: ca = None if images.CARDW >= 43 and ca: if _.max_rounds != 1: images = _.game.app.images _.texts.redeal = MfxCanvasText(_.game.canvas, cx, cy, anchor = ca) _.texts.redeal_str = '' _.texts.redeal.tkraise(_.top_bottom) _.texts.redeal.addtag(_.group) _.top_bottom = _.texts.redeal def getBottomImage(_): return _.game.app.images.getTalonBottom() def getRedealImages(_): return _.game.app.gimages.redeal class DealRowTalonStack(TalonStack): def dealCards(_, sound = 0): return _.dealRowAvail(sound = sound) class InitialDealTalonStack(TalonStack): def initBindings(_): pass def getBottomImage(_): return None class OpenStack(Stack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 1, max_accept = 0, max_cards = 999999) Stack.__init__(_, x, y, game, cap = cap) def acceptsCards(_, from_stack, cards): return _.basicAcceptsCards(from_stack, cards) def canMoveCards(_, cards): return _.basicCanMoveCards(cards) def canFlipCard(_): if _.basicIsBlocked() or not (_.cards): return 0 return not (_.cards[-1].face_up) def canDropCards(_, stacks): if _.basicIsBlocked() or not (_.cards): return (None, 0) cards = _.cards[-1:] if _.canMoveCards(cards): for s in stacks: pass return (None, 0) def clickHandler(_, event): (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event) if _ in flipstacks and _.canFlipCard(): _.playFlipMove() return -1 return 0 def rightclickHandler(_, event): if _.doubleclickHandler(event): return 1 if _.game.app.opt.quickplay: (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event) if _ in quickstacks: n = _.quickPlayHandler(event) _.game.stats.quickplay_moves = _.game.stats.quickplay_moves + n return n return 0 def doubleclickHandler(_, event): (flipstacks, dropstacks, quickstacks) = _.game.getAutoStacks(event) if _ in flipstacks and _.canFlipCard(): _.playFlipMove() return -1 if _ in dropstacks: (to_stack, ncards) = _.canDropCards(_.game.s.foundations) if to_stack: _.game.playSample('autodrop', priority = 30) _.playMoveMove(ncards, to_stack, sound = 0) return 1 return 0 def controlclickHandler(_, event): if _.game.app.opt.highlight_cards: return _.highlightMatchingCards(event) return 0 def releaseHandler(_, event, drag, sound = 1): cards = drag.cards if event is not None: (dx, dy) = (event.x - drag.start_x, event.y - drag.start_y) if abs(dx) < 10 and abs(dy) < 10: Stack.releaseHandler(_, event, drag, sound = sound) return None stack = _.game.getClosestStack(cards[0], _) if not stack and stack is _ or not stack.acceptsCards(_, cards): Stack.releaseHandler(_, event, drag, sound = sound) else: _.playMoveMove(len(cards), stack, frames = 0, sound = sound) def quickPlayHandler(_, event, from_stacks = None, to_stacks = None): if from_stacks is None: from_stacks = _.game.sg.dropstacks if to_stacks is None: to_stacks = _.game.s.foundations + _.game.sg.dropstacks moves = [] if moves: moves.sort() moves.reverse() if moves[0][0] >= 0: moves[0][3].playMoveMove(moves[0][2], moves[0][4]) return 1 return 0 class AbstractFoundationStack(OpenStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, suit = suit, base_suit = suit, base_rank = ACE, dir = 1, max_accept = 1, max_cards = 13) apply(OpenStack.__init__, (_, x, y, game), cap) def canDropCards(_, stacks): return (None, 0) def clickHandler(_, event): return 0 def rightclickHandler(_, event): return 0 def quickPlayHandler(_, event, from_stacks = None, to_stacks = None): return 0 def getBottomImage(_): return _.game.app.images.getSuitBottom(_.cap.base_suit) class SS_FoundationStack(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: if (_.cards[-1].rank + _.cap.dir) % _.cap.mod != cards[0].rank: return 0 return 1 class RK_FoundationStack(SS_FoundationStack): def __init__(_, x, y, game, suit = ANY_SUIT, **cap): apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap) def assertStack(_): SS_FoundationStack.assertStack(_) if not __debug__ and _.cap.suit == ANY_SUIT: raise AssertionError if not __debug__ and _.cap.color == ANY_COLOR: raise AssertionError class AC_FoundationStack(SS_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, base_suit = suit) apply(SS_FoundationStack.__init__, (_, x, y, game, ANY_SUIT), cap) def acceptsCards(_, from_stack, cards): if not SS_FoundationStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: if cards[0].color == _.cards[-1].color: return 0 return 1 class SequenceStack_StackMethods: def _isSequence(_, cards): raise SubclassResponsibility def _isAcceptableSequence(_, cards): return _._isSequence(cards) def _isMoveableSequence(_, cards): return _._isSequence(cards) def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not _._isAcceptableSequence(cards): return 0 if _.cards and not _._isAcceptableSequence([ _.cards[-1]] + cards): return 0 return 1 def canMoveCards(_, cards): if _.basicCanMoveCards(cards): pass return _._isMoveableSequence(cards) class BasicRowStack(OpenStack): def __init__(_, x, y, game, **cap): kwdefault(cap, dir = -1, base_rank = ANY_RANK) apply(OpenStack.__init__, (_, x, y, game), cap) _.CARD_YOFFSET = game.app.images.CARD_YOFFSET class SequenceRowStack(SequenceStack_StackMethods, BasicRowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 999999, max_accept = 999999) apply(BasicRowStack.__init__, (_, x, y, game), cap) class AC_RowStack(SequenceRowStack): def _isSequence(_, cards): return isAlternateColorSequence(cards, _.cap.mod, _.cap.dir) class SC_RowStack(SequenceRowStack): def _isSequence(_, cards): return isSameColorSequence(cards, _.cap.mod, _.cap.dir) class SS_RowStack(SequenceRowStack): def _isSequence(_, cards): return isSameSuitSequence(cards, _.cap.mod, _.cap.dir) class RK_RowStack(SequenceRowStack): def _isSequence(_, cards): return isRankSequence(cards, _.cap.mod, _.cap.dir) class FreeCell_AC_RowStack(AC_RowStack): def canMoveCards(_, cards): max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1 if len(cards) <= max_move: pass return AC_RowStack.canMoveCards(_, cards) class FreeCell_SS_RowStack(SS_RowStack): def canMoveCards(_, cards): max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1 if len(cards) <= max_move: pass return SS_RowStack.canMoveCards(_, cards) class Spider_AC_RowStack(AC_RowStack): def _isAcceptableSequence(_, cards): return isRankSequence(cards, _.cap.mod, _.cap.dir) class Spider_SS_RowStack(SS_RowStack): def _isAcceptableSequence(_, cards): return isRankSequence(cards, _.cap.mod, _.cap.dir) class Yukon_AC_RowStack(BasicRowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 999999, max_accept = 999999) apply(BasicRowStack.__init__, (_, x, y, game), cap) def _isSequence(_, c1, c2): if (c1.rank + _.cap.dir) % _.cap.mod == c2.rank: pass return c1.color != c2.color def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if _.cards and not _._isSequence(_.cards[-1], cards[0]): return 0 return 1 class Yukon_SS_RowStack(Yukon_AC_RowStack): def _isSequence(_, c1, c2): if (c1.rank + _.cap.dir) % _.cap.mod == c2.rank: pass return c1.suit == c2.suit class KingAC_RowStack(AC_RowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, base_rank = KING) apply(AC_RowStack.__init__, (_, x, y, game), cap) class KingSS_RowStack(SS_RowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, base_rank = KING) apply(SS_RowStack.__init__, (_, x, y, game), cap) class KingRK_RowStack(RK_RowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, base_rank = KING) apply(RK_RowStack.__init__, (_, x, y, game), cap) class WasteStack(OpenStack): pass class WasteTalonStack(TalonStack): def __init__(_, x, y, game, max_rounds, num_deal = 1, waste = None, **cap): apply(TalonStack.__init__, (_, x, y, game, max_rounds, num_deal), cap) _.waste = waste def prepareStack(_): TalonStack.prepareStack(_) if _.waste is None: _.waste = _.game.s.waste def canDealCards(_): waste = _.waste if _.cards: num_cards = min(len(_.cards), _.num_deal) return len(waste.cards) + num_cards <= waste.cap.max_cards elif waste.cards and _.round != _.max_rounds: return 1 return 0 def dealCards(_, sound = 0): old_state = _.game.enterState(_.game.S_DEAL) num_cards = 0 waste = _.waste if _.cards: if sound and not (_.game.demo): _.game.playSample('dealwaste') num_cards = min(len(_.cards), _.num_deal) if not __debug__ and len(waste.cards) + num_cards <= waste.cap.max_cards: raise AssertionError for i in range(num_cards): _.game.moveMove(1, _, waste, frames = 4, shadow = 0) _.fillStack() elif waste.cards and _.round != _.max_rounds: if sound: _.game.playSample('turnwaste', priority = 20) num_cards = len(waste.cards) _.game.turnStackMove(waste, _, update_flags = 1) _.game.leaveState(old_state) return num_cards class FaceUpWasteTalonStack(WasteTalonStack): def canFlipCard(_): if len(_.cards) > 0: pass return not (_.cards[-1].face_up) def fillStack(_): if _.canFlipCard(): _.game.flipMove(_) class OpenTalonStack(TalonStack, OpenStack): canMoveCards = OpenStack.canMoveCards canDropCards = OpenStack.canDropCards releaseHandler = OpenStack.releaseHandler def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 1) apply(TalonStack.__init__, (_, x, y, game), cap) def canDealCards(_): return 0 def canFlipCard(_): if len(_.cards) > 0: pass return not (_.cards[-1].face_up) def fillStack(_): if _.canFlipCard(): _.game.flipMove(_) def clickHandler(_, event): if _.canDealCards(): return TalonStack.clickHandler(_, event) else: return OpenStack.clickHandler(_, event) class ReserveStack(OpenStack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_accept = 1, max_cards = 1) apply(OpenStack.__init__, (_, x, y, game), cap) def getBottomImage(_): return _.game.app.images.getReserveBottom() class InvisibleStack(Stack): def __init__(_, game, **cap): (x, y) = (-500, -500 - len(game.allstacks)) kwdefault(cap, max_move = 0, max_accept = 0) Stack.__init__(_, x, y, game, cap = cap) def assertStack(_): Stack.assertStack(_) if not __debug__ and not (_.is_visible): raise AssertionError def initBindings(_): pass def getBottomImage(_): return None class StackWrapper: def __init__(_, stack_class, **cap): if not __debug__ and type(stack_class) is types.ClassType: raise AssertionError if not __debug__ and issubclass(stack_class, Stack): raise AssertionError _.stack_class = stack_class _.cap = cap def __call__(_, x, y, game, **cap): c = _.cap.copy() apply(kwdefault, (c,), cap) return apply(_.stack_class, (x, y, game), c) class WeakStackWrapper(StackWrapper): def __call__(_, x, y, game, **cap): apply(kwdefault, (cap,), _.cap) return apply(_.stack_class, (x, y, game), cap) class FullStackWrapper(StackWrapper): def __call__(_, x, y, game, **cap): return apply(_.stack_class, (x, y, game), _.cap) class HintInterface: def __init__(_, game, level): pass def getHints(_, taken_hint = None): return [] class AbstractHint(HintInterface): def __init__(_, game, level): _.game = game _.level = level _.score_flatten_value = 0 if _.level == 0: _.score_flatten_value = 10000 _.bonus_color = None _._AbstractHint__clones = [] _.reset() def __del__(_): _.reset() def reset(_): _.hints = [] _.max_score = 0 _._AbstractHint__destructClones() class AClonedStack: def __init__(_, stack, stackcards): _.__class__ = stack.__class__ stack.copyModel(_) _.cards = stackcards[:] def ClonedStack(_, stack, stackcards): s = _.AClonedStack(stack, stackcards) _._AbstractHint__clones.append(s) return s def __destructClones(_): for s in _._AbstractHint__clones: s.__class__ = _.AClonedStack destruct(s) _._AbstractHint__clones = [] def addHint(_, score, ncards, from_stack, to_stack, text_color = None, forced_move = None): if score < 0: return None _.max_score = max(_.max_score, score) if _.score_flatten_value > 0: score = (score / _.score_flatten_value) * _.score_flatten_value if text_color is None: text_color = _.BLACK if __debug__: if not forced_move is None or len(forced_move) == 7: raise AssertionError pos = -len(_.hints) ah = (int(score), pos, ncards, from_stack, to_stack, text_color, forced_move) _.hints.append(ah) def __returnHints(_): hints = _.hints _.reset() hints.sort() hints.reverse() return hints SCORE_FLIP = 100000 SCORE_DEAL = 0 def getHints(_, taken_hint = None): _.reset() game = _.game if taken_hint and taken_hint[6]: return [ taken_hint[6]] if _.level >= 2: for r in game.allstacks: if r.canFlipCard(): _.addHint(_.SCORE_FLIP, 1, r, r) if _.SCORE_FLIP >= 90000: return _._AbstractHint__returnHints() _.computeHints() if _.level >= 2: if game.canDealCards(): _.addHint(_.SCORE_DEAL, 0, game.s.talon, None) return _._AbstractHint__returnHints() def computeHints(_): pass def _defaultShallMovePile(_, from_stack, to_stack, pile, rpile): if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile): return 0 return 1 def _cautiousShallMovePile(_, from_stack, to_stack, pile, rpile): if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile): return 0 rr = _.ClonedStack(from_stack, stackcards = rpile) if rr.acceptsCards(to_stack, pile): return 0 return 1 def _cautiousDemoShallMovePile(_, from_stack, to_stack, pile, rpile): if from_stack is to_stack or not to_stack.acceptsCards(from_stack, pile): return 0 if _.level >= 2: rr = _.ClonedStack(from_stack, stackcards = rpile) if rr.acceptsCards(to_stack, pile): return 0 return 1 shallMovePile = _defaultShallMovePile def _canDropAllCards(_, from_stack, stacks, stackcards): if not __debug__ and not (from_stack in stacks): raise AssertionError return 0 cards = pile[:] cards.reverse() for card in cards: for s in stacks: pass else: return 0 return 1 K = KING + 1 BLACK = 'black' RED = 'red' BLUE = 'blue' class DefaultHint(AbstractHint): def _preferHighRankMoves(_): return 0 BONUS_DROP_CARD = 300 BONUS_SAME_SUIT_MOVE = 200 BONUS_NORMAL_MOVE = 100 def _getMoveCardBonus(_, r, t, pile, rpile): if not __debug__ and pile: raise AssertionError bonus = 0 if rpile: rr = _.ClonedStack(r, stackcards = rpile) if rr.canDropCards(_.game.s.foundations)[0]: bonus = _.BONUS_DROP_CARD if t.cards and t.cards[-1].suit == pile[0].suit: bonus = bonus + _.BONUS_SAME_SUIT_MOVE + 1 + pile[0].rank elif _._preferHighRankMoves(): bonus = bonus + _.BONUS_NORMAL_MOVE + 1 + pile[0].rank elif rpile: bonus = bonus + _.BONUS_NORMAL_MOVE + (_.K - rpile[-1].rank) else: bonus = bonus + _.BONUS_NORMAL_MOVE + 1 + pile[0].rank return bonus BONUS_FLIP_CARD = 1500 def _getFlipSpecialBonus(_, r, t, pile, rpile): if __debug__: if not pile and rpile: raise AssertionError bonus = max(_.BONUS_FLIP_CARD - len(rpile), 0) return bonus BONUS_CREATE_EMPTY_ROW = 9000 BONUS_CAN_DROP_ALL_CARDS = 4000 BONUS_CAN_CREATE_EMPTY_ROW = 2000 def _getMoveSpecialBonus(_, r, t, pile, rpile): if not rpile: return _.BONUS_CREATE_EMPTY_ROW if not (rpile[-1].face_up): return _._getFlipSpecialBonus(r, t, pile, rpile) if _._canDropAllCards(r, _.game.s.foundations, stackcards = rpile): _.bonus_color = _.RED return _.BONUS_CAN_DROP_ALL_CARDS + _.BONUS_CAN_CREATE_EMPTY_ROW if r.canMoveCards(rpile): for x in _.game.s.rows: if x.acceptsCards(r, rpile): _.bonus_color = _.BLUE return _.BONUS_CAN_CREATE_EMPTY_ROW return 0 def _getMovePileScore(_, score, color, r, t, pile, rpile): if not __debug__ and pile: raise AssertionError _.bonus_color = color b1 = _._getMoveSpecialBonus(r, t, pile, rpile) if __debug__: if b1 <= b1: pass elif not b1 <= 9000: raise AssertionError b2 = _._getMoveCardBonus(r, t, pile, rpile) if __debug__: if b2 <= b2: pass elif not b2 <= 999: raise AssertionError return (score + b1 + b2, _.bonus_color) def _getMoveWasteScore(_, score, color, r, t, pile, rpile): if not __debug__ and pile: raise AssertionError _.bonus_color = color score = 30000 if t.cards: score = 31000 b2 = _._getMoveCardBonus(r, t, pile, rpile) if __debug__: if b2 <= b2: pass elif not b2 <= 999: raise AssertionError return (score + b2, _.bonus_color) def _getDropCardScore(_, score, color, r, t, ncards): if not __debug__ and t is not r: raise AssertionError if ncards > 1: return (93000, color) pile = r.cards c = pile[-1] if t.cap.base_rank < 0: d = len(t.cards) else: d = (c.rank - t.cap.base_rank) % t.cap.mod if d > t.cap.mod / 2: d = d - t.cap.mod if abs(d) <= 1: score = 92000 elif r in _.game.sg.talonstacks: score = 25000 elif len(pile) == 1: score = 91000 elif _._canDropAllCards(r, _.game.s.foundations, stackcards = pile[:-1]): score = 90000 color = _.RED else: score = 50000 score = score + (_.K - c.rank) return (score, color) def computeHints(_): game = _.game _.step010(game.sg.dropstacks, game.s.rows) if not (_.hints) and _.level >= 1: _.step020(game.s.rows, game.s.foundations) if not (_.hints) and _.level >= 1: _.step030(game.s.foundations, game.s.rows, game.sg.dropstacks) if not (_.hints): _.step040(game.s.rows, game.sg.reservestacks) if not (_.hints): _.step050(game.sg.reservestacks, game.s.rows) def step010(_, dropstacks, rows): for r in dropstacks: (t, ncards) = r.canDropCards(_.game.s.foundations) if t: (score, color) = (0, None) (score, color) = _._getDropCardScore(score, color, r, t, ncards) _.addHint(score, ncards, r, t, color) if score >= 90000: break for pile in _.step010b_getPiles(r): pass def step010b_getPiles(_, stack): return (stack.getPile(),) def step010_movePile(_, r, pile, rows): lp = len(pile) lr = len(r.cards) if __debug__: if lp <= lp: pass elif not lp <= lr: raise AssertionError rpile = r.cards[:lr - lp] empty_row_seen = 0 r_is_waste = r in _.game.sg.talonstacks for t in rows: (score, color) = (0, None) if r_is_waste: (score, color) = _._getMoveWasteScore(score, color, r, t, pile, rpile) elif not (t.cards): if lp == lr: continue if empty_row_seen: continue score = 60000 empty_row_seen = 1 else: score = 80000 (score, color) = _._getMovePileScore(score, color, r, t, pile, rpile) _.addHint(score, lp, r, t, color) step020_getPiles = step010b_getPiles def step020(_, rows, foundations): for r in rows: for pile in _.step020_getPiles(r): drop_info = [] i = 0 for c in pile: rr = _.ClonedStack(r, stackcards = [ c]) (stack, ncards) = rr.canDropCards(foundations) i = i + 1 for di in drop_info: c = di[0] sub_pile = pile[di[3] + 1:] if not __debug__ and r.canMoveCards(sub_pile): raise AssertionError 0 for t in rows: score = 40000 score = score + 1000 + (_.K - r.getCard().rank) force = (999999, 0, di[2], r, di[1], _.BLUE, None) _.addHint(score, len(sub_pile), r, t, _.RED, forced_move = force) def step030(_, foundations, rows, dropstacks): for s in foundations: card = s.getCard() for t in rows: tt = _.ClonedStack(t, stackcards = t.cards + [ card]) for r in dropstacks: pile = r.getPile() if not pile: continue if not tt.acceptsCards(r, pile): continue rpile = r.cards[:len(r.cards) - len(pile)] rr = _.ClonedStack(r, stackcards = rpile) if rr.acceptsCards(t, pile): continue score = 20000 + card.rank force = (999999, 0, len(pile), r, t, _.BLUE, None) _.addHint(score, 1, s, t, _.BLUE, forced_move = force) def step040(_, rows, reservestacks): if not reservestacks: return None for r in rows: card = r.getCard() pile = [ card] rpile = r.cards[:len(r.cards) - len(pile)] rr = _.ClonedStack(r, stackcards = rpile) for t in reservestacks: if rr.acceptsCards(t, pile): continue score = 10000 (score, color) = _._getMovePileScore(score, None, r, t, pile, rpile) _.addHint(score, len(pile), r, t, color) def step050(_, reservestacks, rows): if not reservestacks: return None class CautiousDefaultHint(DefaultHint): shallMovePile = DefaultHint._cautiousShallMovePile def _preferHighRankMoves(_): return 1 class KlondikeType_Hint(DefaultHint): pass class YukonType_Hint(CautiousDefaultHint): def step010b_getPiles(_, stack): p = stack.getPile() piles = [] while p: piles.append(p) p = p[1:] return piles class FreeCellType_Hint(CautiousDefaultHint): pass class GolfType_Hint(DefaultHint): pass class SpiderType_Hint(DefaultHint): pass class Game: U_PLAY = 0 U_WON = -2 U_LOST = -3 U_PERFECT = -4 S_INIT = 0 S_DEAL = 16 S_FILL = 32 S_PLAY = 48 S_UNDO = 64 S_REDO = 80 GAME_VERSION = 1 def __init__(_, gameinfo): _.preview = 0 _.random = None _.gameinfo = gameinfo _.id = gameinfo.id if not __debug__ and _.id > 0: raise AssertionError _.busy = 0 _.version = VERSION _.version_tuple = VERSION_TUPLE _.cards = [] _.stackmap = { } _.allstacks = [] _.demo_logo = None _.s = Struct(talon = None, waste = None, foundations = [], rows = [], reserves = [], internals = []) _.sg = Struct(openstacks = [], talonstacks = [], dropstacks = [], reservestacks = [], hp_stacks = []) _.regions = Struct(info = [], remaining = [], data = []) _.reset() def create(_, app): timer = Timer('Game.create') old_busy = _.busy _._Game__createCommon(app) _.setCursor(cursor = CURSOR_WATCH) _.top.wm_title(PACKAGE + ' - ' + _.getTitleName()) _.top.wm_iconname(PACKAGE + ' - ' + _.getTitleName()) if _.app.intro.progress: _.app.intro.progress.update(step = 1) _.createGame() _.sg.openstacks = filter((lambda s: s.cap.max_accept >= s.cap.min_accept), _.sg.openstacks) _.sg.hp_stacks = filter((lambda s: s.cap.max_move >= 2), _.sg.dropstacks) _.allstacks = tuple(_.allstacks) _.s.foundations = tuple(_.s.foundations) _.s.rows = tuple(_.s.rows) _.s.reserves = tuple(_.s.reserves) _.s.internals = tuple(_.s.internals) _.sg.openstacks = tuple(_.sg.openstacks) _.sg.talonstacks = tuple(_.sg.talonstacks) _.sg.dropstacks = tuple(_.sg.dropstacks) _.sg.reservestacks = tuple(_.sg.reservestacks) _.sg.hp_stacks = tuple(_.sg.hp_stacks) for stack in _.allstacks: stack.prepareStack() stack.assertStack() _.optimizeRegions() if not (_.cards): _.cards = _.createCards(progress = _.app.intro.progress) _.initBindings() _.top.wm_geometry('') _.canvas.config(width = _.width, height = _.height) _.stats.update_time = time.time() _.busy = old_busy def initBindings(_): bind(_.canvas, '<1>', _.clickHandler) bind(_.canvas, '<2>', _.clickHandler) bind(_.canvas, '<3>', _.clickHandler) def __createCommon(_, app): _.busy = 1 _.app = app _.top = app.top _.canvas = app.canvas _.filename = '' _.drag = Struct(event = None, timer = None, start_x = 0, start_y = 0, stack = None, cards = [], shadows = [], shade_stack = None, shade_img = None, canshade_stacks = [], noshade_stacks = []) if _.gstats.start_player is None: _.gstats.start_player = _.app.opt.player _.texts = Struct(info = None, help = None, misc = None, score = None, base_rank = None) def createPreview(_, app): timer = Timer('Game.createPreview') old_busy = _.busy _._Game__createCommon(app) _.preview = max(1, _.canvas.preview) _.createGame() _.sg.openstacks = filter((lambda s: s.cap.max_accept >= s.cap.min_accept), _.sg.openstacks) _.sg.hp_stacks = filter((lambda s: s.cap.max_move >= 2), _.sg.dropstacks) for stack in _.allstacks: stack.prepareStack() stack.assertStack() _.optimizeRegions() _.cards = _.createCards() _.busy = old_busy def destruct(_): for obj in _.cards: destruct(obj) for obj in _.allstacks: obj.destruct() destruct(obj) def reset(_, restart = 0): _.filename = '' _.demo = None _.hints = Struct(list = None, index = -1, level = -1) _.saveinfo = Struct(stack_caps = []) _.loadinfo = Struct(stacks = None, talon_round = 1, ncards = 0) _.stats = Struct(hints = 0, highlight_piles = 0, highlight_cards = 0, highlight_samerank = 0, undo_moves = 0, redo_moves = 0, total_moves = 0, player_moves = 0, demo_moves = 0, autoplay_moves = 0, quickplay_moves = 0, goto_bookmark_moves = 0, demo_updated = 0, update_time = time.time(), elapsed_time = 0.0) _.startMoves() if restart: return None _.gstats = Struct(holded = 0, loaded = 0, saved = 0, restarted = 0, goto_bookmark_moves = 0, updated = _.U_PLAY, start_time = time.time(), total_elapsed_time = 0.0, start_player = None) _.gsaveinfo = Struct(bookmarks = { }, comment = '') def getTitleName(_): return _.app.getGameTitleName(_.id) def getGameNumber(_, format): s = str(_.random) if format: return '#' + s return s def setSize(_, w, h): (_.width, _.height) = (int(round(w)), int(round(h))) def setCursor(_, cursor): if _.canvas: _.canvas.config(cursor = cursor) if _.app and _.app.toolbar: _.app.toolbar.setCursor(cursor = cursor) def newGame(_, random = None, restart = 0, autoplay = 1): (old_busy, _.busy) = (_.busy, 1) _.setCursor(cursor = CURSOR_WATCH) _.disableMenus() _.reset(restart = restart) _.resetGame() _.createRandom(random) _.shuffle() if not __debug__ and len(_.s.talon.cards) == _.gameinfo.ncards: raise AssertionError for stack in _.allstacks: stack.updateText() _.updateText() _.updateStatus(player = _.app.opt.player) _.updateStatus(gamenumber = _.getGameNumber(format = 1), moves = 0) _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id)) _.stopSamples() _.moves.state = _.S_INIT _.startGame() if _.gameinfo.si.game_flags & GI.GT_OPEN: if _.s.talon: if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError None if not (_.preview) else _.allstacks _.startMoves() for stack in _.allstacks: stack.updateText() _.updateText() _.updateStatus(moves = 0) _.updateMenus() _.stopSamples() _.setCursor(cursor = _.app.top_cursor) _.stats.update_time = time.time() _.busy = old_busy def restoreGame(_, game, reset = 1): (old_busy, _.busy) = (_.busy, 1) if reset: _.reset() _.resetGame() _.filename = game.filename _.version = game.version _.version_tuple = game.version_tuple _.random = game.random _.moves = game.moves _.stats = game.stats _.gstats = game.gstats _.saveinfo = game.saveinfo _.gsaveinfo = game.gsaveinfo _.s.talon.round = game.loadinfo.talon_round if not __debug__ and len(_.allstacks) == len(game.loadinfo.stacks): raise AssertionError for i in range(len(_.allstacks)): for t in game.loadinfo.stacks[i]: (card_id, face_up) = t card = _.cards[card_id] _.allstacks[i].addCard(card) for stack_id, cap in _.saveinfo.stack_caps: _.allstacks[stack_id].cap.update(cap.__dict__) _._restoreGameHook(game) for stack in _.allstacks: stack.updateText() _.updateText() _.updateStatus(player = _.app.opt.player) _.updateStatus(gamenumber = _.getGameNumber(format = 1), moves = _.moves.index) _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id)) _.setCursor(cursor = _.app.top_cursor) _.stats.update_time = time.time() _.busy = old_busy def restoreGameFromBookmark(_, bookmark): (old_busy, _.busy) = (_.busy, 1) file = StringIO.StringIO(bookmark) p = Unpickler(file) game = _._undumpGame(p, _.app) _.restoreGame(game, reset = 0) destruct(game) _.busy = old_busy def resetGame(_): _.hints.list = None _.s.talon.removeAllCards() for stack in _.allstacks: stack.resetGame() def nextGameFlags(_, id, random = None): f = 0 if id != _.id: f = f | 1 if _.app.nextgame.cardset is not _.app.cardset: f = f | 2 if random is not None: if random.__class__ is not _.random.__class__: f = f | 16 elif random.initial_seed != _.random.initial_seed: f = f | 16 return f def quitGame(_, id = 0, random = None, loadedgame = None, startdemo = 0, bookmark = 0, holdgame = 0): _.updateTime() if bookmark: (id, random) = (_.id, _.random) file = StringIO.StringIO() p = Pickler(file, 1) _._dumpGame(p, bookmark = 1) _.app.nextgame.bookmark = file.getvalue() if id > 0: _.setCursor(cursor = CURSOR_WATCH) _.app.nextgame.id = id _.app.nextgame.random = random _.app.nextgame.loadedgame = loadedgame _.app.nextgame.startdemo = startdemo _.app.nextgame.holdgame = holdgame _.updateStatus(moves = None, gamenumber = None, stats = None) _.top.mainquit() def endGame(_, restart = 0, bookmark = 0, holdgame = 0): if _.preview: return None _.app.wm_save_state() if holdgame: return None if bookmark: return None if restart: if _.moves.index > 0 and _.getPlayerMoves() > 0: _.gstats.restarted = _.gstats.restarted + 1 return None _.updateStats() stats = _.app.stats if _.shallUpdateBalance(): b = _.getGameBalance() if b: stats.total_balance[_.id] = stats.total_balance.get(_.id, 0) + b stats.session_balance[_.id] = stats.session_balance.get(_.id, 0) + b stats.gameid_balance = stats.gameid_balance + b def restartGame(_): _.endGame(restart = 1) _.newGame(restart = 1, random = _.random) def createRandom(_, random): if random is None: if isinstance(_.random, LCRandom64): seed = _.random.getSeed() _.app.gamerandom.setSeed(seed) while 1: dummy = _.app.gamerandom.random() seed = _.app.gamerandom.getSeed() if seed >= 0x2386F26FC10000L: break _.random = LCRandom64(seed) _.random.origin = _.random.ORIGIN_RANDOM else: _.random = random _.random.reset() def enterState(_, state): old_state = _.moves.state if state < old_state: _.moves.state = state return old_state def leaveState(_, old_state): _.moves.state = old_state def createCards(_, progress = None): timer = Timer('Game.createCards') gi = _.gameinfo pstep = 0 if progress: pstep = (100.0 - progress.percent) / gi.ncards cards = [] id = 0 (x, y) = (_.s.talon.x, _.s.talon.y) for deck in range(gi.decks): for suit in gi.suits: for rank in gi.ranks: card = _._createCard(id, deck, suit, rank, x = x, y = y) cards.append(card) id = id + 1 trump_suit = len(gi.suits) for rank in gi.trumps: card = _._createCard(id, deck, trump_suit, rank, x = x, y = y) cards.append(card) id = id + 1 if progress: progress.update(percent = 100) if not __debug__ and len(cards) == gi.ncards: raise AssertionError return cards def _createCard(_, id, deck, suit, rank, x, y): return Card(id, deck, suit, rank, game = _, x = x, y = y) def shuffle(_): cards = list(_.cards)[:] _.random.reset() _.random.shuffle(cards) cards = _._shuffleHook(cards) for card in cards: _.s.talon.addCard(card, update = 0) card.showBack(unhide = 0) def shuffleSeparateDecks(_): cards = [] _.random.reset() n = _.gameinfo.ncards / _.gameinfo.decks for deck in range(_.gameinfo.decks): i = deck * n deck_cards = list(_.cards)[i:i + n] _.random.shuffle(deck_cards) cards.extend(deck_cards) cards = _._shuffleHook(cards) for card in cards: _.s.talon.addCard(card, update = 0) card.showBack(unhide = 0) def _shuffleHook(_, cards): return cards def _shuffleHookMoveToTop(_, cards, func, ncards = 999999): (cards, scards) = _._shuffleHookMoveSorter(cards, func, ncards) return cards + scards def _shuffleHookMoveToBottom(_, cards, func, ncards = 999999): (cards, scards) = _._shuffleHookMoveSorter(cards, func, ncards) return scards + cards def _shuffleHookMoveSorter(_, cards, func, ncards): (sitems, i) = ([], len(cards)) for c in cards[:]: (select, sort_order) = func(c) if select: cards.remove(c) sitems.append((sort_order, i, c)) if len(sitems) >= ncards: break i = i - 1 sitems.sort() sitems.reverse() scards = map((lambda item: item[2]), sitems) return (cards, scards) def _finishDrag(_): if _.demo: _.stopDemo() if _.busy: return 1 if _.drag.stack: _.drag.stack.finishDrag() return 0 def _cancelDrag(_): if _.demo: _.stopDemo() if _.busy: return 1 if _.drag.stack: _.drag.stack.cancelDrag() return 0 def updateMenus(_): if not (_.preview): _.app.menubar.updateMenus() def disableMenus(_): if not (_.preview): _.app.menubar.disableMenus() def clickHandler(_, *args): if _.demo: _.stopDemo() return EVENT_PROPAGATE def updateStatus(_, **kw): if _.preview: return None (tb, sb) = (_.app.toolbar, _.app.statusbar) for k, v in kw.items(): if k == 'gamenumber': if v is None: continue if type(v) is types.StringType: if sb: sb.updateText(gamenumber = v) continue if k == 'info': if v is None: if sb: sb.updateText(info = '') continue if type(v) is types.StringType: if sb: sb.updateText(info = v) continue if k == 'moves': if v is None: if sb: sb.updateText(moves = '') continue if type(v) is types.IntType: if sb: sb.updateText(moves = 'Moves %d' % v) continue if type(v) is types.StringType: if sb: sb.updateText(moves = v) continue if k == 'player': if v is None: if tb: tb.updateText(player = 'Player\n') continue if type(v) is types.StringType: if tb: if _.app.opt.toolbar_size: tb.updateText(player = 'Player\n' + v) else: tb.updateText(player = v) continue if k == 'stats': if v is None: if sb: sb.updateText(stats = '') continue if type(v) is types.TupleType: t = '%d: %d/%d' % (v[0] + v[1], v[0], v[1]) if sb: sb.updateText(stats = t) continue if k == 'time': continue raise AttributeError, k def playSample(_, name, priority = 0, loop = 0): if _.app.audio: return _.app.audio.playSample(name, priority = priority, loop = loop) return 0 def stopSamples(_): if _.app.audio: _.app.audio.stopSamples() def stopSamplesLoop(_): if _.app.audio: _.app.audio.stopSamplesLoop() def startDealSample(_): a = _.app.opt.animations if a and not (_.preview): _.canvas.update_idletasks() if _.app.audio and _.app.opt.sound: if a in (1, 2, 5): _.playSample('deal01', priority = 100, loop = 1) elif a == 3: _.playSample('deal04', priority = 100, loop = 1) elif a == 4: _.playSample('deal08', priority = 100, loop = 1) def areYouSure(_, title = None, text = None, confirm = -1, default = 0): if _.preview: return 1 if confirm < 0: confirm = _.app.opt.confirm if confirm: if not title: title = PACKAGE if not text: text = 'Discard current game ?' _.playSample('areyousure') d = MfxDialog(_.top, title = title, text = text, bitmap = 'questhead', default = default, strings = ('OK', 'Cancel')) if d.status != 0 or d.button != 0: return 0 return 1 def notYetImplemented(_): d = MfxDialog(_.top, title = 'Not yet implemented', text = 'This function is\nnot yet implemented.', bitmap = 'error') def animatedMoveTo(_, from_stack, to_stack, cards, x, y, tkraise = 1, frames = -1, shadow = -1): if _.app.opt.animations == 0 or frames == 0: return None if _.app.debug and not _.top.winfo_ismapped(): return None (clock, delay, skip) = (None, 1, 1) if _.app.opt.animations >= 2: clock = uclock SPF = 0.15 / 8 if frames < 0: frames = 8 if not __debug__ and frames >= 2: raise AssertionError if _.app.opt.animations == 3: frames = frames * 8 SPF = SPF / 2 elif _.app.opt.animations == 4: frames = frames * 16 SPF = SPF / 2 elif _.app.opt.animations == 5: if _.moves.state == _.S_INIT and frames > 4: frames = frames / 2 if shadow < 0: shadow = _.app.opt.shadow shadows = () c0 = cards[0] (dx, dy) = ((x - c0.x) / float(frames), (y - c0.y) / float(frames)) (tx, ty) = (0, 0) i = 1 if clock: starttime = clock() while i < frames: (mx, my) = (int(round(dx * i)) - tx, int(round(dy * i)) - ty) (tx, ty) = (tx + mx, ty + my) for s in shadows: s.move(mx, my) for card in cards: card.moveBy(mx, my) _.canvas.update_idletasks() step = 1 i = i + step continue None if clock else shadows for s in shadows: s.delete() (dx, dy) = (x - c0.x, y - c0.y) for card in cards: card.moveBy(dx, dy) _.canvas.update_idletasks() def winAnimation(_, perfect = 0): if not (_.app.opt.animations): return None if _.app.debug and not _.top.winfo_ismapped(): return None old_a = _.app.opt.animations if old_a == 0: _.app.opt.animations = 1 elif old_a == 4: _.app.opt.animations = 3 cards = [] for s in _.allstacks: pass acards = [] for i in range(16): (c, s) = _.app.miscrandom.choice(cards) (sx, sy) = (_.s.talon.x, _.s.talon.y) (w, h) = (_.width, _.height) while cards: t = _.app.miscrandom.choice(cards) (c, s) = t s.removeCard(c, update = 0) cards.remove(t) continue None if c in acards or len(cards) <= 2 else _.allstacks _.app.opt.animations = old_a def sleep(_, seconds): if seconds > 0: if _.top: _.top.sleep(seconds) else: time.sleep(seconds) def getCardFaceImage(_, deck, suit, rank): if _.app.cardset.type == CSI.TYPE_TAROCK: if rank >= 10: rank = rank + 1 return _.app.images.getFace(deck, suit, rank) def getCardBackImage(_, deck, suit, rank): return _.app.images.getBack(deck, suit, rank) def _getClosestStack(_, cx, cy, stacks, dragstack): (closest, cdist) = (None, 999999999) for stack in stacks: dist = (stack.x - cx) ** 2 + (stack.y - cy) ** 2 return closest def getClosestStack(_, card, dragstack): (cx, cy) = (card.x, card.y) for stacks, rect in _.regions.info: pass return _._getClosestStack(cx, cy, _.regions.remaining, dragstack) def setRegion(_, stacks, rect, priority = 0): if not __debug__ and len(stacks) > 0: raise AssertionError if __debug__: if not len(rect) == 4 and rect[0] < rect[2] and rect[1] < rect[3]: raise AssertionError for s in stacks: if __debug__: if not s and s in _.allstacks: raise AssertionError 0 (x, y, r) = (s.x, s.y, rect) if __debug__: if not x >= r[0] and x < r[2] and y >= r[1] and y < r[3]: raise AssertionError stacks for d in _.regions.data: if priority == d[0]: if not __debug__ and not (s in d[2]): raise AssertionError 0 _.regions.data.append((priority, -len(_.regions.data), tuple(stacks), tuple(rect))) def optimizeRegions(_): _.regions.data.sort() _.regions.data.reverse() _.regions.info = [] for d in _.regions.data: _.regions.info.append((d[2], d[3])) _.regions.info = tuple(_.regions.info) remaining = list(_.sg.openstacks)[:] for stacks, rect in _.regions.info: for stack in stacks: while stack in remaining: remaining.remove(stack) continue 0 _.regions.remaining = tuple(remaining) def createGame(_): raise SubclassResponsibility def startGame(_): raise SubclassResponsibility def canDealCards(_): if _.s.talon: pass return _.s.talon.canDealCards() def dealCards(_, sound = 1): if _.s.talon and _.canDealCards(): _.finishMove() old_state = _.enterState(_.S_DEAL) n = _.s.talon.dealCards(sound = sound) _.leaveState(old_state) _.finishMove() if not _.checkForWin(): _.autoPlay() return n return 0 def fillStack(_, stack): pass Hint_Class = DefaultHint def getHintClass(_): return _.Hint_Class def getStrictness(_): return 0 def canSaveGame(_): return 1 def canLoadGame(_, version_tuple, game_version): return _.GAME_VERSION == game_version def canSetBookmark(_): return _.canSaveGame() def canUndo(_): return 1 def canRedo(_): return _.canUndo() def changed(_, restart = 0): if _.gstats.updated < 0: return 0 if _.gstats.loaded > 0: return 0 if not restart: if _.gstats.restarted > 0: return 1 if _.gstats.goto_bookmark_moves > 0: return 1 if _.moves.index == 0 or _.getPlayerMoves() == 0: return 0 return 2 def getWinStatus(_): won = _.isGameWon() != 0 if not won and _.stats.hints > 0 or _.stats.demo_moves > 0: return (won, 0, _.U_LOST) if _.stats.undo_moves == 0 and _.stats.goto_bookmark_moves == 0 and _.stats.highlight_piles == 0 and _.stats.highlight_cards == 0: return (won, 2, _.U_PERFECT) return (won, 1, _.U_WON) def updateStats(_, demo = 0): (won, status, updated) = _.getWinStatus() if demo and _.getPlayerMoves() == 0: _.stats.demo_updated = updated _.app.stats.updateStats(None, _, won) elif _.changed(): _.gstats.updated = updated if _.app.opt.update_player_stats: _.app.stats.updateStats(_.app.opt.player, _, status) _.updateStatus(stats = _.app.stats.getStats(_.app.opt.player, _.id)) elif not demo: if _.app.opt.update_player_stats: if _.gstats.loaded: _.app.stats.updateLog(_.app.opt.player, _, -2) elif _.gstats.updated == 0 and _.stats.demo_updated == 0: _.app.stats.updateLog(_.app.opt.player, _, -1) def checkForWin(_): (won, status, updated) = _.getWinStatus() if not won: return 0 _.finishMove() if _.preview: return 1 if status == 2: _.updateStats() _.playSample('winperfect', priority = 1000) d = MfxDialog(_.top, title = 'Game won', text = '\nCongratulations, this\nwas a truly perfect game !\n\n' + 'Your playing time is ' + _.getTime() + '\n' + 'for ' + str(_.moves.index) + ' moves.\n', strings = ('New game', None, 'Cancel'), image = _.app.gimages.logos[5], separatorwidth = 2) elif status == 1: _.updateStats() _.playSample('winwon', priority = 1000) d = MfxDialog(_.top, title = 'Game won', text = '\nCongratulations, you did it !\n\n' + 'Your playing time is ' + _.getTime() + '\n' + 'for ' + str(_.moves.index) + ' moves.\n', strings = ('New game', None, 'Cancel'), image = _.app.gimages.logos[4], separatorwidth = 2) elif _.gstats.updated < 0: _.playSample('winfinished', priority = 1000) d = MfxDialog(_.top, title = 'Game finished', text = '\nGame finished\n', bitmap = '', strings = ('New game', None, 'Cancel')) else: _.playSample('winlost', priority = 1000) d = MfxDialog(_.top, title = 'Game finished', text = '\nGame finished, but not without my help...\n', bitmap = '', strings = ('New game', 'Restart', 'Cancel')) if d.status == 0 and d.button == 0: _.endGame() if status == 2: _.winAnimation(perfect = 1) elif status == 1: _.winAnimation() _.newGame() elif d.status == 0 and d.button == 1: _.restartGame() return 1 def isGameWon(_): c = 0 for s in _.s.foundations: c = c + len(s.cards) return c == len(_.cards) def getFoundationDir(_): for s in _.s.foundations: pass return 0 def getPlayerMoves(_): player_moves = _.stats.player_moves return player_moves def updateTime(_): t = time.time() d = t - _.stats.update_time if d > 0: _.stats.elapsed_time = _.stats.elapsed_time + d _.gstats.total_elapsed_time = _.gstats.total_elapsed_time + d _.stats.update_time = t def getTime(_): _.updateTime() t = int(round(_.stats.elapsed_time)) if t <= 0: return '0:00' if t < 3600: return '%d:%02d' % (t / 60, t % 60) return '%d:%02d:%02d' % (t / 3600, (t % 3600) / 60, t % 60) def getAutoStacks(_, event = None): return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks) def autoPlay(_, autofaceup = -1, autodrop = -1, autodeal = -1, sound = 1): if _.demo: return 0 (old_busy, _.busy) = (_.busy, 1) if autofaceup < 0: autofaceup = _.app.opt.autofaceup if autodrop < 0: autodrop = _.app.opt.autodrop if autodeal < 0: autodeal = _.app.opt.autodeal moves = _.stats.total_moves n = _._autoPlay(autofaceup, autodrop, autodeal, sound = sound) _.finishMove() _.stats.autoplay_moves = _.stats.autoplay_moves + (_.stats.total_moves - moves) _.busy = old_busy return n def _autoPlay(_, autofaceup, autodrop, autodeal, sound): (flipstacks, dropstacks, quickstacks) = _.getAutoStacks() done_something = 1 while done_something: done_something = 0 if autofaceup and flipstacks: for s in flipstacks: if s.canFlipCard(): s.flipMove() done_something = 1 _.finishMove() if _.checkForWin(): return 1 if autodrop and dropstacks: for s in dropstacks: (to_stack, ncards) = s.canDropCards(_.s.foundations) if to_stack: _.finishMove() s.moveMove(ncards, to_stack) done_something = 1 if _.checkForWin(): return 1 if autodeal: if _._autoDeal(sound = sound): done_something = 1 _.finishMove() if _.checkForWin(): return 1 return 0 def _autoDeal(_, sound = 1): w = _.s.waste if w and len(w.cards) == 0 and _.canDealCards(): return _.dealCards(sound = sound) return 0 def getHighlightPilesStacks(_): if _.sg.hp_stacks: return ((_.sg.hp_stacks, 2),) return () def _highlightCards(_, info, sleep = 1.5): if not info: return 0 items = [] for s, c1, c2, color in info: if __debug__: if not c1 in s.cards and c2 in s.cards: raise AssertionError 0 sy0 = s.CARD_YOFFSET[0] if sy0 >= 0: (x1, y1) = s.getPositionFor(c1) (x2, y2) = s.getPositionFor(c2) if c2 is not s.cards[-1] and sy0 > 0: y2 = y2 + sy0 else: y2 = y2 + _.app.images.CARDH else: (x1, y1) = s.getPositionFor(c2) (x2, y2) = s.getPositionFor(c1) y2 = y2 + _.app.images.CARDH if c2 is not s.cards[-1]: y1 = y1 + _.app.images.CARDH + sy0 x2 = x2 + _.app.images.CARDW r = MfxCanvasRectangle(_.canvas, x1 - 1, y1 - 1, x2 + 1, y2 + 1, width = 4, fill = None, outline = color) r.tkraise(c2.item) items.append(r) if not items: return 0 _.canvas.update_idletasks() _.sleep(sleep) items.reverse() for r in items: r.delete() _.canvas.update_idletasks() return EVENT_HANDLED def highlightPiles(_, stackinfo, sleep = 1.5): stackinfo = _.getHighlightPilesStacks() if not stackinfo: return 0 col = _.app.opt.highlight_piles_colors hi = [] for si in stackinfo: for s in si[0]: pile = s.getPile() return _._highlightCards(hi, sleep) def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 def getQuickPlayScore(_, ncards, from_stack, to_stack): return len(to_stack.cards) != 0 def updateText(_): pass def getGameScore(_): return None def getGameScoreCasino(_): v = -len(_.cards) for s in _.s.foundations: v = v + 5 * len(s.cards) return v def shallUpdateBalance(_): if _.gstats.loaded: return 0 if _.random.origin == _.random.ORIGIN_SELECTED: return 0 return 1 def getGameBalance(_): return 0 def getHints(_, level, taken_hint = None): hint_class = _.getHintClass() if hint_class is None: return None hint = hint_class(_, level) return hint.getHints(taken_hint) def showHint(_, level = 0, sleep = 1.5, taken_hint = None): if _.getHintClass() is None: return None if level != _.hints.level: _.hints.level = level _.hints.list = None if _.hints.list is None: _.hints.list = _.getHints(level, taken_hint) _.hints.index = 0 if not (_.hints.list): return None h = _.hints.list[_.hints.index] _.hints.index = _.hints.index + 1 if _.hints.index >= len(_.hints.list): _.hints.index = 0 (score, pos, ncards, from_stack, to_stack, text_color, forced_move) = h if __debug__: if not from_stack and len(from_stack.cards) >= ncards: raise AssertionError if ncards == 0: if not __debug__ and level >= 2: raise AssertionError if not __debug__ and from_stack is _.s.talon: raise AssertionError return h elif from_stack == to_stack: if not __debug__ and level >= 2: raise AssertionError if __debug__: if not ncards == 1 and len(from_stack.cards) >= ncards: raise AssertionError return h elif not __debug__ and to_stack: raise AssertionError if __debug__: if ncards <= ncards: pass elif not ncards <= len(from_stack.cards): raise AssertionError if not __debug__ and to_stack.acceptsCards(from_stack, from_stack.cards[-ncards:]): raise AssertionError if sleep <= 0.0: return h images = _.app.images (x1, y1) = from_stack.getPositionFor(from_stack.cards[-ncards]) (x2, y2) = to_stack.getPositionFor(to_stack.getCard()) (x1, y1) = (x1 + images.CARD_DX, y1 + images.CARD_DY) (x2, y2) = (x2 + images.CARD_DX, y2 + images.CARD_DY) if ncards == 1: x1 = x1 + images.CARDW / 2 y1 = y1 + images.CARDH / 2 elif from_stack.CARD_XOFFSET[0]: x1 = x1 + from_stack.CARD_XOFFSET[0] / 2 y1 = y1 + images.CARDH / 2 else: x1 = x1 + images.CARDW / 2 y1 = y1 + from_stack.CARD_YOFFSET[0] / 2 x2 = x2 + images.CARDW / 2 y2 = y2 + images.CARDH / 2 arrow = MfxCanvasLine(_.canvas, x1, y1, x2, y2, width = 7, fill = _.app.opt.hintarrow_color, arrow = 'last', arrowshape = (30, 30, 10)) if level == 1 and level > 1: pass info = _.app.opt.demo_score if info and _.app.statusbar and _.app.opt.statusbar: _.app.statusbar.configLabel('info', text = 'Score %6d' % score, fg = text_color) else: info = 0 _.canvas.update_idletasks() _.sleep(sleep) if info: _.app.statusbar.configLabel('info', text = '', fg = '#000000') arrow.delete() _.canvas.update_idletasks() return h def startDemo(_, mixed = 1, level = 2): if not __debug__ and level >= 2: raise AssertionError if not (_.top): return None _.demo = Struct(level = level, mixed = mixed, sleep = _.app.opt.demo_sleep, last_deal = [], hint = None, keypress = None, start_demo_moves = _.stats.demo_moves, info_text = None) _.hints.list = None if 1: _.createDemoInfoText() _.createDemoLogo() after_idle(_.top, _.demoEvent) def stopDemo(_, event = None): if not (_.demo): return None _.canvas.setTopImage(None) _.demo_logo = None _.demo = None _.updateMenus() def demoEvent(_): if not (_.demo) or _.demo.keypress: _.stopDemo() _.updateMenus() return None finished = _.playOneDemoMove(_.demo) _.finishMove() _.top.update_idletasks() _.hints.list = None player_moves = _.getPlayerMoves() (d, status) = (None, 0) bitmap = 'info' timeout = 10000 if player_moves == 0: timeout = 5000 if _.isGameWon(): finished = 1 if not _.top.winfo_ismapped(): status = 2 elif player_moves == 0: _.playSample('autopilotwon') s = _.app.miscrandom.choice(('Great', 'Cool', 'Yeah', 'Wow')) d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = '\nGame solved in ' + str(_.moves.index) + ' moves.\n', image = _.app.gimages.logos[4], strings = (s,), separatorwidth = 2, timeout = timeout) status = d.status else: s = _.app.miscrandom.choice(('OK', 'OK')) text = '\n Game finished \n' if _.app.debug: text = text + '\n%d %d\n' % (_.stats.player_moves, _.stats.demo_moves) d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = text, bitmap = bitmap, strings = (s,), padx = 30, timeout = timeout) status = d.status elif finished: if not _.top.winfo_ismapped(): status = 2 elif player_moves == 0: _.playSample('autopilotlost') s = _.app.miscrandom.choice(('Oh well', "That's life", 'Hmm')) d = MfxDialog(_.top, title = PACKAGE + ' Autopilot', text = "\nThis won't come out...\n", bitmap = bitmap, strings = (s,), padx = 30, timeout = timeout) status = d.status if finished: _.updateStats(demo = 1) if _.demo and status == 2 and not (_.app.debug): if _.stats.demo_moves > _.demo.start_demo_moves: _.app.demo_counter = _.app.demo_counter + 1 if _.app.demo_counter % 3 == 0: if _.top.winfo_ismapped(): status = helpAbout(_.app, timeout = 10000) if _.demo and status == 2: demo = _.demo id = _.id if 1 and demo.mixed and _.app.debug: gl = _.app.gdb.getGamesIdSortedById() gl = _.app.gdb.getGamesIdSortedByName() gl = list(gl) index = (gl.index(_.id) + 1) % len(gl) id = gl[index] elif demo.mixed: gl = _.app.gdb.getGamesIdSortedById() while len(gl) > 1: id = _.app.getRandomGameId() if 0 or id != _.id: break if _.nextGameFlags(id) == 0: _.endGame() _.newGame(autoplay = 0) _.startDemo(mixed = demo.mixed) else: _.endGame() _.stopDemo() _.quitGame(id, startdemo = 1) else: _.stopDemo() if 0 and _.app.debug: _.endGame() _.winAnimation() _.newGame() else: _.top.busyUpdate() if _.demo: after_idle(_.top, _.demoEvent) def playOneDemoMove(_, demo): if _.moves.index > 2000: return 1 sleep = demo.sleep if _.app.debug: if not _.top.winfo_ismapped(): sleep = -1.0 if not (demo.hint) or not demo.hint[6]: if _._autoDeal(sound = 0): return 0 h = _.showHint(demo.level, sleep, taken_hint = demo.hint) demo.hint = h if not h: return 1 (score, pos, ncards, from_stack, to_stack, text_color, forced_move) = h if ncards == 0: if _.dealCards() == 0: return 1 c = _.s.talon.getCard() if c in demo.last_deal: return 1 demo.last_deal.append(c) elif from_stack == to_stack: from_stack.flipMove() demo.last_deal = [] else: from_stack.moveMove(ncards, to_stack, frames = -1) demo.last_deal = [] return 0 def createDemoInfoText(_): return None if not (_.demo) and _.demo.info_text or _.preview: return None tinfo = [ ('sw', 8, _.height - 8), ('se', _.width - 8, _.height - 8), ('nw', 8, 8), ('ne', _.width - 8, 8)] ta = _.getDemoInfoTextAttr(tinfo) if ta: font = getFont('canvas_large') _.demo.info_text = MfxCanvasText(_.canvas, ta[1], ta[2], anchor = ta[0], font = font, text = _.getDemoInfoText()) def getDemoInfoText(_): return _.gameinfo.short_name def getDemoInfoTextAttr(_, tinfo): (items1, items2) = ([], []) for s in _.allstacks: if s.is_visible: items1.append(s) items1.extend(list(s.cards)) if not (s.cards) and s.cap.max_accept > 0: items2.append(s) else: items2.extend(list(s.cards)) ti = _._Game__checkFreeSpaceForDemoInfoText(items1) if ti < 0: ti = _._Game__checkFreeSpaceForDemoInfoText(items2) if ti < 0: return None return tinfo[ti] def __checkFreeSpaceForDemoInfoText(_, items): (CW, CH) = (_.app.images.CARDW, _.app.images.CARDH) (x1, x2) = (3 * CW / 2, _.width - 5 * CW / 2) (y1, y2) = (CH / 2, _.height - 3 * CH / 2) m = [ 1, 1, 1, 1] for c in items: (cx, cy) = (c.x, c.y) if cy >= y2: if cx <= x1: m[0] = 0 elif cx >= x2: m[1] = 0 elif cy <= y1: if cx <= x1: m[2] = 0 elif cx >= x2: m[3] = 0 for mm in m: pass return -1 def createDemoLogo(_): if not (_.app.gimages.demo): return None if _.demo_logo or not (_.app.opt.demo_logo): return None if _.width <= 100 or _.height <= 100: return None n = _.random.initial_seed % len(_.app.gimages.demo) _.demo_logo = _.app.gimages.demo[int(n)] _.canvas.setTopImage(_.demo_logo, cw = _.width, ch = _.height) def startMoves(_): _.moves = Struct(state = _.S_PLAY, history = [], index = 0, current = []) _.stats.undo_moves = 0 _.stats.redo_moves = 0 _.stats.player_moves = 0 _.stats.demo_moves = 0 _.stats.total_moves = 0 _.stats.quickplay_moves = 0 _.stats.goto_bookmark_moves = 0 def __storeMove(_, am): if _.moves.state <= _.moves.state: pass elif _.moves.state <= _.S_PLAY: _.moves.current.append(am) def moveMove(_, ncards, from_stack, to_stack, frames = -1, shadow = -1): if __debug__: if not from_stack and to_stack and from_stack is not to_stack: raise AssertionError if __debug__: if ncards < ncards: pass elif not ncards <= len(from_stack.cards): raise AssertionError am = AMoveMove(ncards, from_stack, to_stack, frames, shadow) _._Game__storeMove(am) am.do(_) _.hints.list = None def flipMove(_, stack): if not __debug__ and stack: raise AssertionError am = AFlipMove(stack) _._Game__storeMove(am) am.do(_) _.hints.list = None def turnStackMove(_, from_stack, to_stack, update_flags = 1): if __debug__: if not from_stack and to_stack and from_stack is not to_stack: raise AssertionError if not __debug__ and len(to_stack.cards) == 0: raise AssertionError am = ATurnStackMove(from_stack, to_stack, update_flags = update_flags) _._Game__storeMove(am) am.do(_) _.hints.list = None def nextRoundMove(_, stack): if not __debug__ and stack: raise AssertionError am = ANextRoundMove(stack) _._Game__storeMove(am) am.do(_) _.hints.list = None def saveSeedMove(_): am = ASaveSeedMove(_) _._Game__storeMove(am) am.do(_) def shuffleStackMove(_, stack): if not __debug__ and stack: raise AssertionError am = AShuffleStackMove(stack, _) _._Game__storeMove(am) am.do(_) _.hints.list = None def updateStackMove(_, stack, flags): if not __debug__ and stack: raise AssertionError am = AUpdateStackMove(stack, flags) _._Game__storeMove(am) am.do(_) def finishMove(_): (current, moves, stats) = (_.moves.current, _.moves, _.stats) if not current: return 0 _.hints.list = None if _.demo: stats.demo_moves = stats.demo_moves + 1 if moves.index == 0: stats.player_moves = 0 else: stats.player_moves = stats.player_moves + 1 if moves.index == 0: stats.demo_moves = 0 stats.total_moves = stats.total_moves + 1 redo = 0 if moves.index + 1 < len(moves.history): (l, m) = (len(current), moves.history[moves.index]) if l == len(m): for i in range(l): a1 = current[i] a2 = m[i] else: redo = 1 if redo: moves.history[moves.index] = current moves.index = moves.index + 1 else: moves.history[moves.index:] = [ current] moves.index = moves.index + 1 if not __debug__ and moves.index == len(moves.history): raise AssertionError moves.current = [] _.updateText() _.updateStatus(moves = moves.index) _.updateMenus() return 1 def undo(_): if not __debug__ and _.canUndo(): raise AssertionError if __debug__: if not _.moves.state == _.S_PLAY and _.moves.current == []: raise AssertionError if __debug__: if _.moves.index <= _.moves.index: pass elif not _.moves.index <= len(_.moves.history): raise AssertionError if _.moves.index == 0: return None _.moves.index = _.moves.index - 1 m = _.moves.history[_.moves.index] m = m[:] m.reverse() _.moves.state = _.S_UNDO for atomic_move in m: atomic_move.undo(_) _.moves.state = _.S_PLAY _.stats.undo_moves = _.stats.undo_moves + 1 _.stats.total_moves = _.stats.total_moves + 1 _.hints.list = None _.updateText() _.updateStatus(moves = _.moves.index) _.updateMenus() def redo(_): if not __debug__ and _.canRedo(): raise AssertionError if __debug__: if not _.moves.state == _.S_PLAY and _.moves.current == []: raise AssertionError if __debug__: if _.moves.index <= _.moves.index: pass elif not _.moves.index <= len(_.moves.history): raise AssertionError if _.moves.index == len(_.moves.history): return None m = _.moves.history[_.moves.index] _.moves.index = _.moves.index + 1 _.moves.state = _.S_REDO for atomic_move in m: atomic_move.redo(_) _.moves.state = _.S_PLAY _.stats.redo_moves = _.stats.redo_moves + 1 _.stats.total_moves = _.stats.total_moves + 1 _.hints.list = None _.updateText() _.updateStatus(moves = _.moves.index) _.updateMenus() def setBookmark(_, n, confirm = 1): _.finishMove() if not _.canSetBookmark(): return 0 if confirm < 0: confirm = _.app.opt.confirm if confirm and _.gsaveinfo.bookmarks.get(n): if not _.areYouSure('Set bookmark', 'Replace existing bookmark %d ?' % (n + 1)): return 0 file = StringIO.StringIO() p = Pickler(file, 1) try: _._dumpGame(p, bookmark = 2) bm = (file.getvalue(), _.moves.index) except: pass _.gsaveinfo.bookmarks[n] = bm return 1 return 0 def gotoBookmark(_, n, confirm = -1, update_stats = 1): _.finishMove() bm = _.gsaveinfo.bookmarks.get(n) if not bm: return None if confirm < 0: confirm = _.app.opt.confirm if confirm: if not _.areYouSure('Goto bookmark', 'Goto bookmark %d ?' % (n + 1)): return None try: (s, moves_index) = bm _.setCursor(cursor = CURSOR_WATCH) file = StringIO.StringIO(s) p = Unpickler(file) game = _._undumpGame(p, _.app) if not __debug__ and game.id == _.id: raise AssertionError _.setBookmark(-1, confirm = 0) except: del _.gsaveinfo.bookmarks[n] _.setCursor(cursor = _.app.top_cursor) if update_stats: _.stats.goto_bookmark_moves = _.stats.goto_bookmark_moves + 1 _.gstats.goto_bookmark_moves = _.gstats.goto_bookmark_moves + 1 _.restoreGame(game, reset = 0) destruct(game) def undoGotoBookmark(_): _.gotoBookmark(-1, update_stats = 0) def loadGame(_, filename): if _.changed(): if not _.areYouSure('Open game'): return None _.finishMove() game = None _.setCursor(cursor = CURSOR_WATCH) _.disableMenus() try: game = _._loadGame(filename, _.app) game.gstats.holded = 0 except AssertionError: ex = None _.updateMenus() _.setCursor(cursor = _.app.top_cursor) d = MfxDialog(_.top, title = 'Load game error', bitmap = 'error', text = 'Error while loading game.\n\nProbably the game file is damaged,\nbut this could also be a bug you might want to report.') except (Exception, UnpicklingError): ex = None _.updateMenus() _.setCursor(cursor = _.app.top_cursor) d = MfxExceptionDialog(_.top, ex, title = 'Load game error', text = 'Error while loading game') except: _.updateMenus() _.setCursor(cursor = _.app.top_cursor) d = MfxDialog(_.top, title = 'Load game error', bitmap = 'error', text = 'Internal error while loading game.\n\nPlease report this bug.') _.filename = filename game.filename = filename if _.nextGameFlags(game.id) == 0: _.endGame() _.restoreGame(game) destruct(game) else: _.endGame() _.quitGame(game.id, loadedgame = game) def saveGame(_, filename, binmode = 1): _.finishMove() _.setCursor(cursor = CURSOR_WATCH) try: _._saveGame(filename, binmode) except Exception: ex = None _.setCursor(cursor = _.app.top_cursor) d = MfxExceptionDialog(_.top, ex, title = 'Save game error', text = 'Error while saving game') _.filename = filename _.setCursor(cursor = _.app.top_cursor) def _loadGame(_, filename, app): game = None f = None try: f = open(filename, 'rb') p = Unpickler(f) game = _._undumpGame(p, app) game.gstats.loaded = game.gstats.loaded + 1 finally: if f: f.close() return game def _getUndumpVersion(_, version_tuple): if version_tuple > (4, 41): return 4 if version_tuple > (4, 30): return 3 if version_tuple > (4, 20): return 2 if version_tuple > (3, 20): return 1 if version_tuple >= (2, 99): return 0 return -1 def _undumpGame(_, p, app): _.updateTime() def check(expr, text = None): if not expr: if not text: text = 'Invalid or damaged ' + PACKAGE + ' save file' raise Exception, str(text) def pload(t = None, p = p): obj = p.load() if type(t) is types.TypeType: if type(obj) is not t: text = 'Invalid or damaged ' + PACKAGE + ' save file' raise Exception, text return obj package = pload() if type(package) is types.StringType: pass check(package == PACKAGE) version = pload() if type(version) is types.StringType: pass check(len(version) <= 20) version_tuple = get_version_tuple(version) v = _._getUndumpVersion(version_tuple) if v >= 0: pass check(version_tuple <= VERSION_TUPLE, 'Cannot load games saved with\n' + PACKAGE + ' version ' + version) game_version = 1 bookmark = 0 if v >= 2: vt = pload() if type(vt) is types.TupleType: pass check(vt == version_tuple) bookmark = pload() if type(bookmark) is types.IntType: pass None(check if bookmark <= bookmark else bookmark <= 2, 'Incompatible savegame format') game_version = pload() if type(game_version) is types.IntType: pass check(game_version > 0) if v <= 3: bookmark = 0 id = pload() if type(id) is types.IntType: pass check(id > 0) if not GI.PROTECTED_GAMES.has_key(id): game = app.constructGame(id) if game: if not game.canLoadGame(version_tuple, game_version): destruct(game) game = None check(game is not None, 'Cannot load this game from version ' + version + '\nas the game rules have changed\nin the current implementation.') game.version = version game.version_tuple = version_tuple game.random = pload() if type(game.random) is types.TupleType: game.random = WHRandom(game.random) check(type(game.random) is types.InstanceType) check(isinstance(game.random, PysolRandom)) if not hasattr(game.random, 'origin'): game.random.origin = game.random.ORIGIN_UNKNOWN game.loadinfo.stacks = [] game.loadinfo.ncards = 0 nstacks = pload() if type(nstacks) is types.IntType: pass None(check if nstacks <= nstacks else nstacks <= 255) for i in range(nstacks): stack = [] ncards = pload() if type(ncards) is types.IntType: pass 0(check if ncards <= ncards else ncards <= 1024) for j in range(ncards): card_id = pload(types.IntType) face_up = pload(types.IntType) stack.append((card_id, face_up)) game.loadinfo.stacks.append(stack) game.loadinfo.ncards = game.loadinfo.ncards + ncards check(game.loadinfo.ncards == game.gameinfo.ncards) game.loadinfo.talon_round = pload() if bookmark <= bookmark: pass elif bookmark <= 1: if v >= 3: saveinfo = pload() check(isinstance(saveinfo, Struct)) merge_dict(game.saveinfo.__dict__, saveinfo.__dict__) if v >= 4: gsaveinfo = pload() check(isinstance(gsaveinfo, Struct)) merge_dict(game.gsaveinfo.__dict__, gsaveinfo.__dict__) elif v >= 1: talon_base_cards = pload(types.ListType) moves = pload() check(isinstance(moves, Struct)) merge_dict(game.moves.__dict__, moves.__dict__) if bookmark <= bookmark: pass elif bookmark <= 1: gstats = pload() check(isinstance(gstats, Struct)) merge_dict(game.gstats.__dict__, gstats.__dict__) stats = pload() check(isinstance(stats, Struct)) merge_dict(game.stats.__dict__, stats.__dict__) game._loadGameHook(p) if v >= 4: dummy = pload(types.StringType) check(dummy == 'EOF') if bookmark == 2: game.stats = _.stats game.gstats = _.gstats game.saveinfo = _.saveinfo game.gsaveinfo = _.gsaveinfo return game def _saveGame(_, filename, binmode = 1): f = None try: if not _.canSaveGame(): raise Exception, 'Cannot save this game.' f = open(filename, 'wb') p = Pickler(f, binmode) _._dumpGame(p) finally: if f: f.close() def _dumpGame(_, p, bookmark = 0): _.updateTime() if __debug__: if bookmark <= bookmark: pass elif not bookmark <= 2: raise AssertionError p.dump(PACKAGE) p.dump(VERSION) p.dump(VERSION_TUPLE) p.dump(bookmark) p.dump(_.GAME_VERSION) p.dump(_.id) p.dump(_.random) p.dump(len(_.allstacks)) for stack in _.allstacks: p.dump(len(stack.cards)) for card in stack.cards: p.dump(card.id) p.dump(card.face_up) p.dump(_.s.talon.round) if bookmark <= bookmark: pass elif bookmark <= 1: p.dump(_.saveinfo) p.dump(_.gsaveinfo) p.dump(_.moves) if bookmark <= bookmark: pass elif bookmark <= 1: p.dump(_.gstats) p.dump(_.stats) _._saveGameHook(p) p.dump('EOF') def _restoreGameHook(_, game): pass def _loadGameHook(_, p): pass def _saveGameHook(_, p): pass class _LayoutStack: def __init__(_, x, y, suit = None): _.x = int(round(x)) _.y = int(round(y)) _.suit = suit _.text_args = { } _.text_format = '%d' def setText(_, x, y, anchor = 'center', format = None, **kw): _.text_args['x'] = x _.text_args['y'] = y _.text_args['anchor'] = anchor _.text_args.update(kw) if format is not None: _.text_format = format class Layout: def __init__(_, game, XM = 10, YM = 10, **kw): _.game = game _.canvas = _.game.canvas _.size = None _.s = Struct(talon = None, waste = None, foundations = [], rows = [], reserves = []) _.stackmap = { } _.regions = [] images = _.game.app.images _.CW = images.CARDW _.CH = images.CARDH _.XM = XM _.YM = YM _.XS = _.CW + XM _.YS = _.CH + YM _.XOFFSET = images.CARD_XOFFSET _.YOFFSET = images.CARD_YOFFSET _.__dict__.update(kw) if _.game.preview > 1: if kw.has_key('XOFFSET'): _.XOFFSET = _.XOFFSET / _.game.preview if kw.has_key('YOFFSET'): _.YOFFSET = _.YOFFSET / _.game.preview def __createStack(_, x, y, suit = None): stack = _LayoutStack(x, y, suit) mapkey = (stack.x, stack.y) if not __debug__ and not _.stackmap.has_key(mapkey): raise AssertionError _.stackmap[mapkey] = stack return stack def getTextAttr(_, stack, anchor): (x, y) = (0, 0) if stack is not None: (x, y) = (stack.x, stack.y) if anchor == 'n': return (x + _.CW / 2, y - _.YM, 'center', '%d') if anchor == 'nn': return (x + _.CW / 2, y - _.YM, 's', '%d') if anchor == 's': return (x + _.CW / 2, y + _.YS, 'center', '%d') if anchor == 'ss': return (x + _.CW / 2, y + _.YS, 'n', '%d') if anchor == 'nw': return (x - _.XM, y, 'ne', '%d') if anchor == 'sw': return (x - _.XM, y + _.CH, 'se', '%d') f = '%2d' if _.game.gameinfo.decks > 1: f = '%3d' if anchor == 'ne': return (x + _.XS, y, 'nw', f) if anchor == 'se': return (x + _.XS, y + _.CH, 'sw', f) if anchor == 'e': return (x + _.XS, y + _.CH / 2, 'w', f) raise Exception, anchor def createText(_, stack, anchor, dx = 0, dy = 0, text_format = ''): if _.canvas.preview > 1: return None if not __debug__ and stack.texts.ncards is None: raise AssertionError (tx, ty, ta, tf) = _.getTextAttr(stack, anchor) stack.texts.ncards = MfxCanvasText(_.canvas, tx + dx, ty + dy, anchor = ta) if not text_format: pass stack.texts.ncards.text_format = tf def setRegion(_, stacks, rects): _.regions.append((stacks, rects)) def defaultAll(_): game = _.game if game.s.talon: game.s.talon.texts.ncards = _.defaultText(_.s.talon) if game.s.waste: game.s.waste.texts.ncards = _.defaultText(_.s.waste) _.defaultStackGroups() _.defaultRegions() def defaultText(_, layout_stack): if _.canvas.preview > 1: return None if layout_stack is None or not (layout_stack.text_args): return None t = apply(MfxCanvasText, (_.game.canvas,), layout_stack.text_args) t.text_format = layout_stack.text_format return t def defaultStackGroups(_): game = _.game waste = [] if game.s.waste is not None: waste = [ game.s.waste] game.sg.talonstacks = [ game.s.talon] + waste game.sg.dropstacks = game.s.rows + game.s.reserves + waste game.sg.openstacks = game.s.foundations + game.s.rows + game.s.reserves game.sg.reservestacks = game.s.reserves def defaultRegions(_): for region in _.regions: stacks = [] for s in region[0]: mapkey = (s.x, s.y) id = _.game.stackmap[mapkey] stacks.append(_.game.allstacks[id]) _.game.setRegion(stacks, region[1]) def bakersDozenLayout(_, rows, texts = 0, playcards = 9): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) halfrows = (rows + 1) / 2 h = YS + min(2 * YS, (playcards - 1) * _.YOFFSET) h = max(h, 5 * YS / 2, 3 * YS / 2 + CH) h = min(h, 3 * YS) (x, y) = (XM, YM) for i in range(halfrows): _.s.rows.append(S(x + i * XS, y)) for i in range(rows - halfrows): _.s.rows.append(S(x + i * XS, y + h)) (x, y) = (XM + halfrows * XS, YM) _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999)) for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x + i * XS, y, suit = suit)) y = y + YS h = YM + 2 * h _.size = (XM + (halfrows + decks) * XS, h) def freeCellLayout(_, rows, reserves, texts = 0, playcards = 18): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) toprows = reserves + 1 + suits * decks maxrows = max(rows, toprows) w = XM + maxrows * XS h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = YM + YS + max(h, 3 * YS) (x, y) = ((w - toprows * XS - XM) / 2, YM) for i in range(reserves): _.s.reserves.append(S(x, y)) x = x + XS for suit in range(suits): for i in range(decks): x = x + XS _.s.foundations.append(S(x, y, suit = suit)) (x, y) = ((w - rows * XS - XM) / 2, YM + YS) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999)) (x, y) = (XM, h - YS) _.size = (XM + (rows + decks) * XS, h) def gypsyLayout(_, rows, waste = 0, texts = 1, playcards = 25): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = YM + max(h, (suits + 1) * YS) (x, y) = (XM, YM) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999)) for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x + i * XS, y, suit = suit)) y = y + YS (x, y) = (x + (decks - 1) * XS, h - YS) _.size = (XM + (rows + decks) * XS, h) def harpLayout(_, rows, waste, texts = 1, playcards = 19): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) w = max(rows * XS, (suits * decks + waste + 1) * XS, (suits * decks + 1) * XS + 2 * XM) w = XM + w h = YS + (playcards - 1) * _.YOFFSET h = max(h, 3 * YS) (x, y) = ((w - rows * XS - XM) / 2, YM) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS (x, y) = (XM, YM + h) _.setRegion(_.s.rows, (-999, -999, 999999, y - YS / 2)) for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x, y, suit = suit)) x = x + XS x = w - XS _.size = (w, YM + h + YS) def klondikeLayout(_, rows, waste, texts = 1, playcards = 16, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) frows = suits * decks toprows = 1 + waste + frows maxrows = max(rows, toprows) yextra = 0 h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) (x, y) = (XM, YM) _.s.talon = s = S(x, y) if texts: if waste and not center or maxrows - frows <= 1: s.setText(x + CW / 2, y + YS, anchor = 'n') yextra = 20 else: s.setText(x + XS, y, anchor = 'nw', format = '%3d') if waste: x = x + XS _.s.waste = s = S(x, y) if texts: s.setText(x + CW / 2, y + YS, anchor = 'n') x = XM + (maxrows - frows) * XS if center and frows + 2 * (1 + waste + 1) <= maxrows: x = XM + (maxrows - frows) * XS / 2 for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x, y, suit = suit)) x = x + XS (x, y) = (XM, YM + YS + yextra) _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999)) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.size = (XM + maxrows * XS, YM + YS + yextra + h) def yukonLayout(_, rows, texts = 0, playcards = 20): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = YM + max(h, suits * YS) (x, y) = (XM, YM) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (-999, -999, x - CW / 2, 999999)) for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x + i * XS, y, suit = suit)) y = y + YS (x, y) = (XM, h - YS) _.size = (XM + (rows + decks) * XS, h) def easyLayout(_, rows, waste, texts = 1, playcards = 10, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) frows = 4 * decks / (1 + (decks >= 3)) toprows = 1 + waste + frows maxrows = max(rows, toprows) yextra = 0 h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) (x, y) = (XM, YM) _.s.talon = s = S(x, y) if texts: if waste and not center or maxrows - frows <= 1: s.setText(x + CW / 2, y + YS, anchor = 'n') yextra = 20 else: s.setText(x + XS, y, anchor = 'nw', format = '%3d') if waste: x = x + XS _.s.waste = s = S(x, y) if texts: s.setText(x + CW / 2, y + YS, anchor = 'n') x = XM + (maxrows - frows) * XS if center and frows + 2 * (1 + waste + 1) <= maxrows: x = XM + (maxrows - frows) * XS / 2 (x0, y0) = (x, y) for suit in range(suits): for i in range(decks): _.s.foundations.append(S(x0, y0, suit = suit)) x0 = x0 + XS (x, y) = (XM, y + YS + yextra * (decks <= 2)) _.setRegion(_.s.rows, (-999, y - YM / 2, 999999, 999999)) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.size = (XM + maxrows * XS, YM + YS + yextra + h) def samuriLayout(_, rows, waste, texts = 1, playcards = 20, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) toprows = 2 * decks + rows yextra = 0 h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) x = XM + toprows * XS / 2 - XS y = h _.s.talon = s = S(x, y) if texts: if waste and not center or toprows - rows <= 1: s.setText(x + CW / 2, y + YS, anchor = 'n') yextra = 20 else: s.setText(x + XS, y, anchor = 'nw', format = '%3d') if waste: x = x + XS _.s.waste = s = S(x, y) if texts: s.setText(x + CW / 2, y + YS, anchor = 'n') (x, y) = (XM, YM) (d, x0, y0) = (0, x, y) for suit in range(12): for i in range(decks): (x0, y0) = (x + XS * i, y + YS * d) _.s.foundations.append(S(x0, y0, suit = suit)) d = d + 1 (x, y) = (XM + XS * decks, YM) _.setRegion(_.s.rows, (x - XM / 2, 0, x + XS * rows, 999999)) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.size = (XM + toprows * XS, YM + YS + yextra + h) def sumoLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) if not __debug__ and reserves % 2 == 0: raise AssertionError toprows = 12 maxrows = max(rows, toprows) w = XM + maxrows * XS h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) (x, y) = (XM, YM) for i in range(decks): for suit in range(12): _.s.foundations.append(S(x, y, suit = suit)) x = x + XS (x, y) = (XM, y + YS) (x, y) = (XM + XS * ((toprows - rows) / 2), YM + YS * decks) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (XS + XM / 2, YS * decks + YM / 2, XS * 11 - XM / 2, 999999)) (x, y) = (XM, YM + YS * decks) for i in range(reserves / 2): _.s.reserves.append(S(x, y)) y = y + YS (x, y) = (w - XS, YM + YS * decks) for i in range(reserves / 2): _.s.reserves.append(S(x, y)) y = y + YS (x, y) = (XM, h) _.size = (XM + toprows * XS, YM + YS + h) def funLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) if not __debug__ and rows % 2 == 0: raise AssertionError if not __debug__ and reserves % decks == 0: raise AssertionError toprows = decks + rows / 2 w = XM * 2 + toprows * XS h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) (x, y) = (w - XS * decks, YM) for i in range(decks): for suit in range(suits): _.s.foundations.append(S(x, y, suit = suit)) y = y + YS (x, y) = (x + XS, YM) (x, y) = (XM, YM) for i in range(rows / 2): _.s.rows.append(S(x, y)) x = x + XS (x, y) = (XM, (YS + h) / 2) for i in range(rows / 2): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (0, 0, XS * rows / 2 + XM / 2, 999999)) (x, y) = (w - XS * decks, YM + YS * 4) for i in range(decks): for i in range(reserves / decks): _.s.reserves.append(S(x, y)) y = y + YS (x, y) = (x + XS, YM + YS * 4) (x, y) = (XM, h) _.size = (w, YM + YS + h) def oonsooLayout(_, rows, reserves, texts = 0, playcards = 12, center = 1): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks if not __debug__ and rows % 2 == 0: raise AssertionError toprows = decks + rows / 2 w = XM * 2 + toprows * (XS + XM) h = CH * 2 / 3 + (playcards - 1) * _.YOFFSET h = max(h, 2 * YS) (x, y) = (XM, YM) _.s.talon = s = S(x, y) if texts: s.setText(x + CW / 2, y + YS, anchor = 'n', format = '%d') (x, y) = (XS + XM * 3, YM) for i in range(rows / 2): _.s.rows.append(S(x, y)) x = x + XS + XM (x, y) = (XS + XM * 3, (YS + h) / 2) for i in range(rows / 2): _.s.rows.append(S(x, y)) x = x + XS + XM _.setRegion(_.s.rows, (XS + XM, -999, 999999, 999999)) (x, y) = (XM, YM * 2 + YS) for i in range(decks): for i in range(reserves / decks): _.s.reserves.append(S(x, y)) y = y + YS (x, y) = (x + XS, YM + YS * 4) _.size = (w, YM + YS + h) def ghulamLayout(_, rows, reserves = 0, texts = 0): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks if not __debug__ and rows % 2 == 0: raise AssertionError if not __debug__ and reserves % 2 == 0: raise AssertionError (w, h) = (XM * 3 + XS * 9, YM + YS * 6) (x, y) = (XM, YM) for i in range(8): _.s.foundations.append(S(x, y, suit = i)) y = y + YS x = XM * 2 + XS for i in range(rows / 2): _.s.rows.append(S(x + i * XS, YM)) for i in range(rows / 2): _.s.rows.append(S(x + i * XS, h / 2)) _.setRegion(_.s.rows, (XM + XS, -999, w - XM - XS, 999999)) for i in range(reserves / 2): _.s.reserves.append(S(XM, h - YS * (i + 1))) for i in range(reserves / 2): _.s.reserves.append(S(w - XS, h - YS * (i + 1))) _.size = (w, h) def generiklonLayout(_, rows, waste = 1, height = 6): S = _._Layout__createStack (CW, CH) = (_.CW, _.CH) (XM, YM) = (_.XM, _.YM) (XS, YS) = (_.XS, _.YS) decks = _.game.gameinfo.decks suits = len(_.game.gameinfo.suits) + bool(_.game.gameinfo.trumps) frows = suits * decks / 2 fspace = XS * (rows - 1) / 2 (w, h) = (XM + XS * rows, YM * 2 + YS * height) _.size = (w, h) (x, y) = (XM, YM) _.s.talon = s = S(x, y) s.setText(x + XS, y + CH, anchor = 'sw', format = '%3d') _.s.waste = s = S(x, y + YS) s.setText(x + XS, y + YS + CH, anchor = 'sw', format = '%3d') x = w - fspace - XS * frows / 2 for suit in range(suits / 2): for i in range(decks): _.s.foundations.append(S(x, y, suit = suit)) x = x + XS x = w - fspace - XS * frows / 2 y = y + YS for suit in range(suits / 2): for i in range(decks): _.s.foundations.append(S(x, y, suit = suit + suits / 2)) x = x + XS (x, y) = (XM, YM * 2 + YS * 2) for i in range(rows): _.s.rows.append(S(x, y)) x = x + XS _.setRegion(_.s.rows, (-999, y - YM, 999999, 999999)) class Klondike(Game): Layout_Method = Layout.klondikeLayout Talon_Class = WasteTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = KingAC_RowStack Hint_Class = KlondikeType_Hint def createGame(_, max_rounds = -1, num_deal = 1, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 7, waste = 1, texts = 1, playcards = 16) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _, max_rounds = max_rounds, num_deal = num_deal) if l.s.waste: s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() return l def startGame(_, flip = 0, reverse = 1): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(reverse = reverse) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class VegasKlondike(Klondike): getGameScore = Game.getGameScoreCasino getGameBalance = Game.getGameScoreCasino def createGame(_, max_rounds = 1): Klondike.createGame(_, max_rounds = max_rounds) _.texts.score = MfxCanvasText(_.canvas, 8, _.height - 8, anchor = 'sw', font = getFont('canvas_large')) def updateText(_): if _.preview > 1: return None (b1, b2) = (_.app.stats.gameid_balance, 0) if _.shallUpdateBalance(): b2 = _.getGameBalance() if 0 and _.app.debug: t = 'Balance %d/%d' % (b1, b2) else: t = 'Balance $%d' % (b1 + b2) _.texts.score.config(text = t) def getDemoInfoTextAttr(_, tinfo): return tinfo[1] class CasinoKlondike(VegasKlondike): def createGame(_): VegasKlondike.createGame(_, max_rounds = 3) class KlondikeByThrees(Klondike): def createGame(_): Klondike.createGame(_, num_deal = 3) class ThumbAndPouch_RowStack(SequenceRowStack): def _isSequence(_, cards): return isAnySuitButOwnSequence(cards, _.cap.mod, _.cap.dir) class ThumbAndPouch(Klondike): RowStack_Class = ThumbAndPouch_RowStack def createGame(_): Klondike.createGame(_, max_rounds = 1) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit != card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Whitehead_RowStack(SS_RowStack): def _isAcceptableSequence(_, cards): return isSameColorSequence(cards, _.cap.mod, _.cap.dir) class Whitehead(Klondike): RowStack_Class = Whitehead_RowStack Hint_Class = CautiousDefaultHint def createGame(_): Klondike.createGame(_, max_rounds = 1) def startGame(_): Klondike.startGame(_, flip = 1) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class SmallHarp(Klondike): Layout_Method = Layout.gypsyLayout def startGame(_): for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() class Eastcliff(Klondike): RowStack_Class = AC_RowStack def createGame(_): Klondike.createGame(_, max_rounds = 1) def startGame(_): for i in range(2): _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class Easthaven(Eastcliff): Talon_Class = DealRowTalonStack def createGame(_): Klondike.createGame(_, max_rounds = 1, waste = 0) class Westcliff(Eastcliff): Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0) def createGame(_): Klondike.createGame(_, max_rounds = 1, rows = 10) class Westhaven(Westcliff): Talon_Class = DealRowTalonStack def createGame(_): Klondike.createGame(_, max_rounds = 1, rows = 10, waste = 0) class PasSeul(Eastcliff): def createGame(_): Klondike.createGame(_, max_rounds = 1, rows = 6) def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() class BlindAlleys(Eastcliff): def createGame(_): Klondike.createGame(_, max_rounds = 2, rows = 6) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.s.talon.dealRow(rows = _.s.foundations, frames = 0) Eastcliff.startGame(_) class Somerset(Klondike): Talon_Class = InitialDealTalonStack RowStack_Class = StackWrapper(AC_RowStack, max_move = 1) Hint_Class = CautiousDefaultHint def createGame(_): Klondike.createGame(_, max_rounds = 1, rows = 10, waste = 0, texts = 0) def startGame(_): for i in range(6): _.s.talon.dealRow(rows = _.s.rows[i:], frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[6:]) _.s.talon.dealRow(rows = _.s.rows[7:]) class Canister(Klondike): Talon_Class = InitialDealTalonStack RowStack_Class = RK_RowStack def createGame(_): Klondike.createGame(_, max_rounds = 1, rows = 8, waste = 0, texts = 0) def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.rows[2:6]) class AgnesSorel(Klondike): Talon_Class = DealRowTalonStack Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, max_move = 0) RowStack_Class = StackWrapper(SC_RowStack, mod = 13, base_rank = NO_RANK) def createGame(_): Klondike.createGame(_, max_rounds = 1, waste = 0) def startGame(_): Klondike.startGame(_, flip = 1) c = _.s.talon.dealSingleBaseCard() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color == card2.color and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank class EightTimesEight(Klondike): Layout_Method = Layout.gypsyLayout RowStack_Class = AC_RowStack def createGame(_): Klondike.createGame(_, rows = 8) def startGame(_): for i in range(7): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() class AchtmalAcht(EightTimesEight): def createGame(_): l = Klondike.createGame(_, rows = 8, max_rounds = 3) s = _.s (x, y) = (s.waste.x - l.XM, s.waste.y) s.talon.texts.rounds = MfxCanvasText(_.canvas, x, y, anchor = 'ne') class Batsford_ReserveStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 return cards[0].rank == KING class Batsford(Klondike): def createGame(_, **layout): l = Klondike.createGame(_, rows = 10, max_rounds = 1, playcards = 22) s = _.s (x, y) = (l.XM, _.height - l.YS) s.reserves.append(Batsford_ReserveStack(x, y, _, max_cards = 3)) _.setRegion(s.reserves, (-999, y - l.YM, x + l.XS, 999999), priority = 1) l.createText(s.reserves[0], 'se') l.defaultStackGroups() class Stonewall(Klondike): Talon_Class = InitialDealTalonStack RowStack_Class = AC_RowStack DEAL = (0, 1, 0, 1, -1, 0, 1) def createGame(_): l = Klondike.createGame(_, rows = 6, max_rounds = 1, texts = 0) s = _.s h = max(_.height, l.YM + 4 * l.YS) _.setSize(_.width + l.XM + 4 * l.XS, h) for i in range(4): for j in range(4): (x, y) = (_.width + (j - 4) * l.XS, l.YM + i * l.YS) s.reserves.append(OpenStack(x, y, _, max_accept = 0)) l.defaultStackGroups() def startGame(_): frames = 0 for flip in _.DEAL: pass _.s.talon.dealRow(rows = _.s.reserves) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError None if flip < 0 else _.DEAL class FlowerGarden(Stonewall): RowStack_Class = StackWrapper(RK_RowStack, max_move = 1) Hint_Class = CautiousDefaultHint DEAL = (1, 1, 1, 1, -1, 1, 1) class KingAlbert(Klondike): Talon_Class = InitialDealTalonStack RowStack_Class = StackWrapper(AC_RowStack, max_move = 1) Hint_Class = CautiousDefaultHint ROWS = 9 RESERVES = (2, 2, 2, 1) def createGame(_): l = Klondike.createGame(_, max_rounds = 1, rows = _.ROWS, waste = 0, texts = 0) s = _.s (rw, rh) = (max(_.RESERVES), len(_.RESERVES)) h = max(_.height, l.YM + rh * l.YS) _.setSize(_.width + 2 * l.XM + rw * l.XS, h) for i in range(rh): for j in range(_.RESERVES[i]): (x, y) = (_.width + (j - rw) * l.XS, l.YM + i * l.YS) s.reserves.append(OpenStack(x, y, _, max_accept = 0)) l.defaultStackGroups() def startGame(_): Klondike.startGame(_, flip = 1, reverse = 0) _.s.talon.dealRow(rows = _.s.reserves) class Raglan(KingAlbert): RESERVES = (2, 2, 2) def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): for i in range(6): _.s.talon.dealRow(rows = _.s.rows[i:], frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[6:]) _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealRow(rows = _.s.foundations) class Brigade(Raglan): RowStack_Class = StackWrapper(RK_RowStack, max_move = 1) ROWS = 7 RESERVES = (4, 4, 4, 1) def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealRow(rows = _.s.foundations) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Jane_Talon(OpenTalonStack): def canFlipCard(_): return 0 def canDealCards(_): return len(_.cards) >= 2 def dealCards(_, sound = 0): c = 0 if len(_.cards) > 2: c = _.dealRow(_.game.s.reserves, sound = sound) if len(_.cards) == 2: _.game.flipMove(_) _.game.moveMove(1, _, _.game.s.waste, frames = 4, shadow = 0) _.game.flipMove(_) c = c + 1 return c class Jane(Klondike): Talon_Class = Jane_Talon Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, min_cards = 1) RowStack_Class = StackWrapper(AC_RowStack, mod = 13, base_rank = NO_RANK) def createGame(_, max_rounds = 1, reserves = 7, **layout): kwdefault(layout, texts = 0) l = apply(Klondike.createGame, (_, max_rounds), layout) s = _.s h = max(_.height, l.YM + 4 * l.YS) _.setSize(_.width + l.XM + 2 * l.XS, h) (x0, y) = (_.width - 2 * l.XS, l.YM) for i in range(reserves): x = x0 + (i + 1 & 1) * l.XS stack = OpenStack(x, y, _, max_accept = 0) stack.CARD_YOFFSET = l.YM / 3 stack.is_open = 1 s.reserves.append(stack) y = y + l.YS / 2 l.defaultStackGroups() _.sg.dropstacks.append(s.talon) (x, y) = (l.XM, _.height - l.YM) _.texts.info = MfxCanvasText(_.canvas, x, y, anchor = 'sw', font = getFont('canvas_card', cardw = l.CW)) def startGame(_, flip = 0, reverse = 1): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(reverse = reverse) _.s.talon.dealRow(rows = _.s.reserves) c = _.s.talon.dealSingleBaseCard() cap = Struct(base_rank = (c.rank - 1) % 13) for s in _.s.rows: s.cap.update(cap.__dict__) _.saveinfo.stack_caps.append((s.id, cap)) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def _autoDeal(_, sound = 1): return 0 class AgnesBernauer_Talon(DealRowTalonStack): def dealCards(_, sound = 0): return _.dealRowAvail(_.game.s.reserves, sound = sound) class AgnesBernauer(Jane): Talon_Class = AgnesBernauer_Talon Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK, max_move = 0) def createGame(_): Jane.createGame(_, max_rounds = 1, waste = 0, texts = 1) def startGame(_): Jane.startGame(_, flip = 1) registerGame(GameInfo(2, Klondike, 'Klondike', GI.GT_KLONDIKE, 1, -1)) registerGame(GameInfo(61, CasinoKlondike, 'Casino Klondike', GI.GT_KLONDIKE | GI.GT_SCORE, 1, 2)) registerGame(GameInfo(129, VegasKlondike, 'Vegas Klondike', GI.GT_KLONDIKE | GI.GT_SCORE, 1, 0)) registerGame(GameInfo(18, KlondikeByThrees, 'Klondike by Threes', GI.GT_KLONDIKE, 1, -1)) registerGame(GameInfo(58, ThumbAndPouch, 'Thumb and Pouch', GI.GT_KLONDIKE, 1, 0)) registerGame(GameInfo(67, Whitehead, 'Whitehead', GI.GT_KLONDIKE, 1, 0)) registerGame(GameInfo(39, SmallHarp, 'Small Harp', GI.GT_KLONDIKE, 1, -1)) registerGame(GameInfo(66, Eastcliff, 'Eastcliff', GI.GT_KLONDIKE, 1, 0)) registerGame(GameInfo(224, Easthaven, 'Easthaven', GI.GT_GYPSY, 1, 0)) registerGame(GameInfo(33, Westcliff, 'Westcliff', GI.GT_KLONDIKE, 1, 0)) registerGame(GameInfo(225, Westhaven, 'Westhaven', GI.GT_GYPSY, 1, 0)) registerGame(GameInfo(107, PasSeul, 'Pas Seul', GI.GT_KLONDIKE, 1, 0)) registerGame(GameInfo(81, BlindAlleys, 'Blind Alleys', GI.GT_KLONDIKE, 1, 1)) registerGame(GameInfo(215, Somerset, 'Somerset', GI.GT_KLONDIKE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(231, Canister, 'Canister', GI.GT_KLONDIKE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(229, AgnesSorel, 'Agnes Sorel', GI.GT_GYPSY, 1, 0)) registerGame(GameInfo(4, EightTimesEight, '8 x 8', GI.GT_KLONDIKE, 2, -1)) registerGame(GameInfo(127, AchtmalAcht, 'Achtmal Acht', GI.GT_KLONDIKE, 2, 2)) registerGame(GameInfo(133, Batsford, 'Batsford', GI.GT_KLONDIKE, 2, 0)) registerGame(GameInfo(221, Stonewall, 'Stonewall', GI.GT_RAGLAN, 1, 0)) registerGame(GameInfo(222, FlowerGarden, 'Flower Garden', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(233, KingAlbert, 'King Albert', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(232, Raglan, 'Raglan', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(223, Brigade, 'Brigade', GI.GT_RAGLAN | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(230, Jane, 'Jane', GI.GT_RAGLAN, 1, 0)) registerGame(GameInfo(236, AgnesBernauer, 'Agnes Bernauer', GI.GT_RAGLAN, 1, 0)) class Gypsy(Game): Layout_Method = Layout.gypsyLayout Talon_Class = DealRowTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack Hint_Class = KlondikeType_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 8, waste = 0, texts = 1) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) if l.s.waste: s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() def startGame(_): for i in range(2): _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Giant_Foundation(SS_FoundationStack): def canMoveCards(_, cards): if not SS_FoundationStack.canMoveCards(_, cards): return 0 return len(_.game.s.talon.cards) == 0 class Giant(Gypsy): Foundation_Class = Giant_Foundation def startGame(_): _.startDealSample() _.s.talon.dealRow() class Irmgard_Talon(TalonStack): def dealCards(_, sound = 0): if _.cards: if len(_.cards) > 7: c = _.dealRow(sound = sound) else: c = _.dealRow(_.game.s.rows[1:8], sound = sound) return c return 0 class Irmgard(Gypsy): Layout_Method = Layout.harpLayout Talon_Class = Irmgard_Talon def createGame(_): Gypsy.createGame(_, rows = 9, playcards = 19) def startGame(_): r = _.s.rows for i in range(1, 5): _.s.talon.dealRow(rows = r[i:len(r) - i], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class DieKoenigsbergerin_Talon(DealRowTalonStack): dealToStacks = DealRowTalonStack.dealToStacksOrFoundations class DieKoenigsbergerin(Gypsy): Talon_Class = DieKoenigsbergerin_Talon Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0) def startGame(_): _.startDealSample() for i in range(3): _.s.talon.dealRow() class DieRussische_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: rank = _.cards[-1].rank if rank == ACE: rank = 5 if (rank + _.cap.dir) % _.cap.mod != cards[0].rank: return 0 return 1 class DieRussische_RowStack(AC_RowStack): def acceptsCards(_, from_stack, cards): if not AC_RowStack.acceptsCards(_, from_stack, cards): return 0 if not _.cards: pass return len(cards) == 1 class DieRussische(Gypsy): Talon_Class = InitialDealTalonStack Foundation_Class = StackWrapper(DieRussische_Foundation, min_cards = 1) RowStack_Class = DieRussische_RowStack def createGame(_): Gypsy.createGame(_, rows = 7, texts = 0) def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)), 1) def startGame(_): for i in range(6): _.s.talon.dealRow(frames = 0) _.startDealSample() for i in range(3): _.s.talon.dealRow() c = _.s.talon.cards[-1] _.s.talon.dealRow(rows = (_.s.foundations[c.suit * 2],)) class MissMilligan_ReserveStack(AC_RowStack): def acceptsCards(_, from_stack, cards): if not AC_RowStack.acceptsCards(_, from_stack, cards): return 0 if len(_.cards) == 0: pass return len(_.game.s.talon.cards) == 0 def getBottomImage(_): return _.game.app.images.getReserveBottom() class MissMilligan(Gypsy): Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0) RowStack_Class = KingAC_RowStack ReserveStack_Class = MissMilligan_ReserveStack def createGame(_, rows = 8, reserves = 1): (l, s) = (Layout(_), _.s) _.setSize(l.XM + (1 + max(8, rows)) * l.XS, l.YM + (1 + max(4, reserves)) * l.YS) (x, y) = (l.XM, l.YM) s.talon = _.Talon_Class(x, y, _) for i in range(8): x = x + l.XS s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2)) (x, y) = (l.XM, y + l.YS) (rx, ry) = (x + l.XS - l.XM / 2, y - l.YM / 2) for i in range(reserves): s.reserves.append(_.ReserveStack_Class(x, y, _)) y = y + l.YS (x, y) = (l.XM + (8 - rows) * l.XS / 2, l.YM + l.YS) for i in range(rows): x = x + l.XS s.rows.append(_.RowStack_Class(x, y, _)) _.setRegion(s.rows, (rx, ry, 999999, 999999)) l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() class Nomad(MissMilligan): Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack ReserveStack_Class = ReserveStack def startGame(_): for i in range(3): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() class MilliganCell(MissMilligan): ReserveStack_Class = ReserveStack def createGame(_): MissMilligan.createGame(_, reserves = 4) def startGame(_): _.startDealSample() _.s.talon.dealRow() class MilliganHarp(Gypsy): Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0) def startGame(_, flip = 0): for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = flip, frames = 0) _.startDealSample() _.s.talon.dealRow() class Carlton(MilliganHarp): def startGame(_): MilliganHarp.startGame(_, flip = 1) class LexingtonHarp(MilliganHarp): GAME_VERSION = 2 RowStack_Class = Yukon_AC_RowStack Hint_Class = YukonType_Hint class Brunswick(LexingtonHarp): def startGame(_): LexingtonHarp.startGame(_, flip = 1) class Mississippi(LexingtonHarp): def createGame(_): LexingtonHarp.createGame(_, rows = 7) class Griffon(Mississippi): def startGame(_): Mississippi.startGame(_, flip = 1) class Blockade(Gypsy): Layout_Method = Layout.klondikeLayout RowStack_Class = SS_RowStack def createGame(_): Gypsy.createGame(_, rows = 12) def startGame(_): _.startDealSample() _.s.talon.dealRow() def fillStack(_, stack): if stack in _.s.rows and not (stack.cards) and _.s.talon.cards: old_state = _.enterState(_.S_FILL) _.s.talon.flipMove() _.s.talon.moveMove(1, stack) _.leaveState(old_state) registerGame(GameInfo(1, Gypsy, 'Gypsy', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(65, Giant, 'Giant', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(3, Irmgard, 'Irmgard', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(119, DieKoenigsbergerin, 'Die K\xf6nigsbergerin', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(174, DieRussische, 'Die Russische', GI.GT_2DECK_TYPE | GI.GT_OPEN, 2, 0, ranks = (0, 6, 7, 8, 9, 10, 11, 12))) registerGame(GameInfo(62, MissMilligan, 'Miss Milligan', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(200, Nomad, 'Nomad', GI.GT_GYPSY | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0)) registerGame(GameInfo(78, MilliganCell, 'Milligan Cell', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(217, MilliganHarp, 'Milligan Harp', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(218, Carlton, 'Carlton', GI.GT_GYPSY, 2, 0)) registerGame(GameInfo(68, LexingtonHarp, 'Lexington Harp', GI.GT_YUKON, 2, 0)) registerGame(GameInfo(154, Brunswick, 'Brunswick', GI.GT_YUKON, 2, 0)) registerGame(GameInfo(226, Blockade, 'Blockade', GI.GT_GYPSY, 2, 0)) class FortyThieves_Hint(CautiousDefaultHint): pass class FortyThieves(Game): Foundation_Class = SS_FoundationStack RowStack_Class = SS_RowStack Hint_Class = FortyThieves_Hint FOUNDATION_MAX_MOVE = 1 ROW_MAX_MOVE = 1 DEAL = (0, 4) FILL_EMPTY_ROWS = 0 def createGame(_, max_rounds = 1, num_deal = 1, rows = 10, playcards = 12, XCARDS = 64, XOFFSET = 10): XM = (10, 4)[rows > 10] (l, s) = (Layout(_, XM = XM, XOFFSET = XOFFSET, YBOTTOM = 16), _.s) decks = _.gameinfo.decks maxrows = max(rows, 4 * decks + 2) (w1, w2) = (maxrows * l.XS + l.XM, 2 * l.XS) if w2 + XCARDS * l.XOFFSET > w1: l.XOFFSET = int((w1 - w2) / XCARDS) h = max(2 * l.YS, l.YS + (playcards - 1) * l.YOFFSET) _.setSize(w1, l.YM + l.YS + h + l.YS + l.YBOTTOM) x = l.XM + (maxrows - 4 * decks) * l.XS / 2 y = l.YM for i in range(4 * decks): s.foundations.append(_.Foundation_Class(x, y, _, suit = i / decks, max_move = _.FOUNDATION_MAX_MOVE)) x = x + l.XS x = l.XM + (maxrows - rows) * l.XS / 2 y = l.YM + l.YS for i in range(rows): s.rows.append(_.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE)) x = x + l.XS x = _.width - l.XS y = _.height - l.YS - l.YBOTTOM s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal) l.createText(s.talon, 's') x = x - l.XS s.waste = WasteStack(x, y, _) s.waste.CARD_XOFFSET = -(l.XOFFSET) l.createText(s.waste, 's') l.defaultStackGroups() def startGame(_): for i in range(_.DEAL[0]): _.s.talon.dealRow(flip = 0, frames = 0) for i in range(_.DEAL[1] - 1): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def fillStack(_, stack): if _.FILL_EMPTY_ROWS and stack in _.s.rows and not (stack.cards): old_state = _.enterState(_.S_FILL) if _.s.waste.cards: _.s.waste.moveMove(1, stack) elif _.s.talon.canDealCards(): _.s.talon.dealCards() _.s.waste.moveMove(1, stack) _.leaveState(old_state) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class BusyAces(FortyThieves): DEAL = (0, 1) def createGame(_): FortyThieves.createGame(_, rows = 12) class Limited(BusyAces): DEAL = (0, 3) class Courtyard(BusyAces): ROW_MAX_MOVE = 999999 FILL_EMPTY_ROWS = 1 class WaningMoon(FortyThieves): def createGame(_): FortyThieves.createGame(_, rows = 13) class Lucas(WaningMoon): ROW_MAX_MOVE = 999999 class Deuces(FortyThieves): Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = 1) RowStack_Class = StackWrapper(SS_RowStack, mod = 13) DEAL = (0, 1) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 1, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) FortyThieves.startGame(_) class Corona(FortyThieves): FOUNDATION_MAX_MOVE = 0 DEAL = (0, 3) FILL_EMPTY_ROWS = 1 def createGame(_): FortyThieves.createGame(_, rows = 12) class Quadrangle(Corona): Foundation_Class = StackWrapper(SS_FoundationStack, mod = 13, base_rank = NO_RANK) RowStack_Class = StackWrapper(SS_RowStack, mod = 13) def startGame(_): FortyThieves.startGame(_) _.s.talon.dealSingleBaseCard() class FortyAndEight(FortyThieves): def createGame(_): FortyThieves.createGame(_, max_rounds = 2, rows = 8, XCARDS = 72, XOFFSET = 8) class LittleForty(FortyThieves): RowStack_Class = Spider_SS_RowStack ROW_MAX_MOVE = 999999 FILL_EMPTY_ROWS = 1 def createGame(_): FortyThieves.createGame(_, max_rounds = 4, num_deal = 3, XOFFSET = 0) class Streets(FortyThieves): RowStack_Class = AC_RowStack def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Maria(Streets): def createGame(_): Streets.createGame(_, rows = 9) class NumberTen(Streets): ROW_MAX_MOVE = 999999 DEAL = (2, 2) class RankAndFile(Streets): ROW_MAX_MOVE = 999999 DEAL = (3, 1) class TripleLine(Streets): GAME_VERSION = 2 FOUNDATION_MAX_MOVE = 0 ROW_MAX_MOVE = 999999 DEAL = (0, 3) FILL_EMPTY_ROWS = 1 def createGame(_): Streets.createGame(_, max_rounds = 2, rows = 12) class RedAndBlack(Streets): Foundation_Class = AC_FoundationStack ROW_MAX_MOVE = 999999 DEAL = (0, 1) def createGame(_): FortyThieves.createGame(_, rows = 8) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) Streets.startGame(_) class Zebra(RedAndBlack): FOUNDATION_MAX_MOVE = 0 ROW_MAX_MOVE = 1 FILL_EMPTY_ROWS = 1 def createGame(_): FortyThieves.createGame(_, max_rounds = 2, rows = 8, XOFFSET = 0) class Indian_RowStack(SequenceRowStack): def _isSequence(_, cards): return isAnySuitButOwnSequence(cards, _.cap.mod, _.cap.dir) class Indian(FortyThieves): RowStack_Class = Indian_RowStack DEAL = (1, 2) def createGame(_): FortyThieves.createGame(_, XCARDS = 74, XOFFSET = 8) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit != card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Midshipman(Indian): DEAL = (2, 2) def createGame(_): FortyThieves.createGame(_, rows = 9) class NapoleonsExile(FortyThieves): RowStack_Class = RK_RowStack DEAL = (0, 4) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class DoubleRail(NapoleonsExile): ROW_MAX_MOVE = 999999 DEAL = (0, 1) def createGame(_): FortyThieves.createGame(_, rows = 5) class SingleRail(DoubleRail): def createGame(_): FortyThieves.createGame(_, rows = 4, XCARDS = 48) registerGame(GameInfo(13, FortyThieves, 'Forty Thieves', GI.GT_FORTY_THIEVES, 2, 0, altnames = ('Napoleon at St.Helena', 'Le Cadran'))) registerGame(GameInfo(80, BusyAces, 'Busy Aces', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(228, Limited, 'Limited', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(79, WaningMoon, 'Waning Moon', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(125, Lucas, 'Lucas', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(109, Deuces, 'Deuces', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(196, Corona, 'Corona', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(195, Quadrangle, 'Quadrangle', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(110, Courtyard, 'Courtyard', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(23, FortyAndEight, 'Forty and Eight', GI.GT_FORTY_THIEVES, 2, 1)) registerGame(GameInfo(115, LittleForty, 'Little Forty', GI.GT_FORTY_THIEVES, 2, 3)) registerGame(GameInfo(76, Streets, 'Streets', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(73, Maria, 'Maria', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(70, NumberTen, 'Number Ten', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(71, RankAndFile, 'Rank and File', GI.GT_FORTY_THIEVES, 2, 0, altnames = 'Emperor')) registerGame(GameInfo(126, RedAndBlack, 'Red and Black', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(113, Zebra, 'Zebra', GI.GT_FORTY_THIEVES, 2, 1)) registerGame(GameInfo(69, Indian, 'Indian', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(74, Midshipman, 'Midshipman', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(131, DoubleRail, 'Double Rail', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(199, SingleRail, 'Single Rail', GI.GT_FORTY_THIEVES, 1, 0)) class Diplomat(Game): Foundation_Class = SS_FoundationStack RowStack_Class = RK_RowStack Hint_Class = FortyThieves_Hint FOUNDATION_MAX_MOVE = 0 ROW_MAX_MOVE = 1 DEAL = (3, 1) FILL_EMPTY_ROWS = 0 def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 5 * l.YS) (x, y) = (l.XM, l.YM) for i in range(8): s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2, max_move = _.FOUNDATION_MAX_MOVE)) x = x + l.XS (x, y) = (l.XM, y + l.YS) for i in range(8): s.rows.append(_.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE)) x = x + l.XS (x, y) = (l.XM, _.height - l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'nn') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'nn') l.defaultStackGroups() def startGame(_): for i in range(_.DEAL[0]): _.s.talon.dealRow(frames = 0) _.startDealSample() for i in range(_.DEAL[1]): _.s.talon.dealRow() _.s.talon.dealCards() def fillStack(_, stack): if _.FILL_EMPTY_ROWS and stack in _.s.rows and not (stack.cards): old_state = _.enterState(_.S_FILL) if _.s.waste.cards: _.s.waste.moveMove(1, stack) elif _.s.talon.canDealCards(): _.s.talon.dealCards() _.s.waste.moveMove(1, stack) _.leaveState(old_state) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class LadyPalk(Diplomat): ROW_MAX_MOVE = 999999 class Congress(Diplomat): DEAL = (0, 1) FILL_EMPTY_ROWS = 1 def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS) for i in range(4): for j in range(2): (x, y) = (l.XM + (4 + j) * l.XS, l.YM + i * l.YS) s.foundations.append(_.Foundation_Class(x, y, _, suit = i, max_move = _.FOUNDATION_MAX_MOVE)) for i in range(4): for j in range(2): (x, y) = (l.XM + (3 + 3 * j) * l.XS, l.YM + i * l.YS) stack = _.RowStack_Class(x, y, _, max_move = _.ROW_MAX_MOVE) stack.CARD_YOFFSET = 0 s.rows.append(stack) (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() registerGame(GameInfo(149, Diplomat, 'Diplomat', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(151, LadyPalk, 'Lady Palk', GI.GT_FORTY_THIEVES, 2, 0)) registerGame(GameInfo(150, Congress, 'Congress', GI.GT_FORTY_THIEVES, 2, 0)) class DoubleKlondike(Game): Layout_Method = Layout.harpLayout RowStack_Class = KingAC_RowStack Hint_Class = KlondikeType_Hint def createGame(_, max_rounds = -1, num_deal = 1, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 9, waste = 1, texts = 1, playcards = 19) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = WasteTalonStack(l.s.talon.x, l.s.talon.y, _, max_rounds = max_rounds, num_deal = num_deal) s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for r in l.s.foundations: s.foundations.append(SS_FoundationStack(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() if max_rounds > 1: if not __debug__ and s.talon.texts.rounds is None: raise AssertionError 0 (tx, ty, ta, tf) = l.getTextAttr(s.talon, 'nn') s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, ty, anchor = ta) return l def startGame(_): for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class DoubleKlondikeByThrees(DoubleKlondike): def createGame(_): DoubleKlondike.createGame(_, num_deal = 3) class Gargantua(DoubleKlondike): def createGame(_): DoubleKlondike.createGame(_, max_rounds = 2) class BigHarp(DoubleKlondike): RowStack_Class = AC_RowStack def createGame(_): DoubleKlondike.createGame(_, max_rounds = 1, rows = 10) def startGame(_): for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() class Steps(DoubleKlondike): RowStack_Class = AC_RowStack def createGame(_): DoubleKlondike.createGame(_, max_rounds = 1, rows = 7) registerGame(GameInfo(21, DoubleKlondike, 'Double Klondike', GI.GT_KLONDIKE, 2, -1)) registerGame(GameInfo(28, DoubleKlondikeByThrees, 'Double Klondike by Threes', GI.GT_KLONDIKE, 2, -1)) registerGame(GameInfo(25, Gargantua, 'Gargantua', GI.GT_KLONDIKE, 2, 1)) registerGame(GameInfo(15, BigHarp, 'Big Harp', GI.GT_KLONDIKE, 2, 0)) registerGame(GameInfo(51, Steps, 'Steps', GI.GT_KLONDIKE, 2, 0)) class PictureGallery_Hint(AbstractHint): def computeHints(_): game = _.game for r in game.sg.dropstacks: (t, n) = r.canDropCards(game.s.foundations) if not (_.hints): for r in game.sg.dropstacks: pile = r.getPile() for t in game.s.tableaux: pass if not (_.hints): for r in game.s.tableaux: pile = r.getPile() rr = _.ClonedStack(r, stackcards = r.cards[:-1]) if rr.acceptsCards(None, pile): continue for t in game.s.rows: pass if not (_.hints): for r in game.s.rows: pile = r.getPile() base_score = 60000 for t in game.s.rows: pass if _.level >= 2: if game.canDealCards(): _.addHint(_.SCORE_DEAL, 0, game.s.talon, None) class PictureGallery_Foundation(RK_FoundationStack): def __init__(_, x, y, game): RK_FoundationStack.__init__(_, x, y, game, base_rank = ACE, dir = 0, max_move = 0, max_cards = 8) _.CARD_YOFFSET = min(30, _.game.app.images.CARD_YOFFSET + 10) def getBottomImage(_): return _.game.app.images.getLetter(ACE) class PictureGallery_TableauStack(SS_RowStack): def __init__(_, x, y, game, base_rank, yoffset): SS_RowStack.__init__(_, x, y, game, base_rank = base_rank, dir = 3, max_accept = 1) _.CARD_YOFFSET = yoffset def acceptsCards(_, from_stack, cards): if not SS_RowStack.acceptsCards(_, from_stack, cards): return 0 if _.cards and _.cards[0].rank != _.cap.base_rank: return 0 return 1 def getBottomImage(_): return _.game.app.images.getLetter(_.cap.base_rank) class PictureGallery_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if _.cards or _.game.s.talon.cards: return 0 return 1 def getBottomImage(_): return _.game.app.images.getTalonBottom() class PictureGallery(Game): Hint_Class = PictureGallery_Hint def createGame(_): (l, s) = (Layout(_), _.s) TABLEAU_YOFFSET = min(9, max(3, l.YOFFSET / 3)) th = l.YS + 3 * TABLEAU_YOFFSET h = (10 - 1) * l.YOFFSET + l.CH * 2 / 3 _.setSize(10 * l.XS + l.XM, l.YM + 3 * th + l.YM + h) s.addattr(tableaux = []) x = l.XM + 8 * l.XS + l.XS / 2 y = l.YM + l.CH / 2 s.foundations.append(PictureGallery_Foundation(x, y, _)) y = l.YM for i in (3, 2, 1): x = l.XM for j in range(8): s.tableaux.append(PictureGallery_TableauStack(x, y, _, i, yoffset = TABLEAU_YOFFSET)) x = x + l.XS y = y + th (x, y) = (l.XM, y + l.YM) for i in range(8): s.rows.append(PictureGallery_RowStack(x, y, _, max_accept = 1)) x = x + l.XS x = l.XM + 8 * l.XS + l.XS / 2 y = _.height - l.YS s.talon = DealRowTalonStack(x, y, _) l.createText(s.talon, 'se') _.setRegion(s.foundations, (x - l.CW / 2, -999, 999999, y - l.CH)) _.sg.openstacks = s.foundations + s.tableaux + s.rows _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.tableaux + s.rows def startGame(_): _.s.talon.dealRow(rows = _.s.tableaux, frames = 0) _.startDealSample() _.s.talon.dealRow() def isGameWon(_): if len(_.s.foundations[0].cards) != 8: return 0 for stack in _.s.tableaux: pass return 1 def fillStack(_, stack): if _.s.talon.cards: if stack in _.s.rows and len(stack.cards) == 0: _.s.talon.dealRow(rows = [ stack]) def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank == ACE or card2.rank == ACE: return 0 if not card1.suit == card2.suit and card1.rank + 3 == card2.rank: pass return card2.rank + 3 == card1.rank def getHighlightPilesStacks(_): return () registerGame(GameInfo(7, PictureGallery, 'Picture Gallery', GI.GT_2DECK_TYPE, 2, 0)) class Braid_Hint(DefaultHint): pass class Braid_Foundation(AbstractFoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, mod = 13, dir = 0, base_rank = NO_RANK, max_move = 0) apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap) def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if not (_.cards): return 1 stack_dir = _.game.getFoundationDir() if stack_dir == 0: card_dir = _.getRankDir(cards = (_.cards[-1], cards[0])) return card_dir in (1, -1) else: return (_.cards[-1].rank + stack_dir) % _.cap.mod == cards[0].rank class Braid_BraidStack(OpenStack): def __init__(_, x, y, game, sine = 0): OpenStack.__init__(_, x, y, game) _.CARD_YOFFSET = _.game.app.images.CARD_YOFFSET CW = _.game.app.images.CARDW class Braid_RowStack(ReserveStack): def fillStack(_): if not (_.cards) and _.game.s.braid.cards: _.game.moveMove(1, _.game.s.braid, _) def getBottomImage(_): return _.game.app.images.getBraidBottom() class Braid_ReserveStack(ReserveStack): def acceptsCards(_, from_stack, cards): if from_stack is _.game.s.braid or from_stack in _.game.s.rows: return 0 return ReserveStack.acceptsCards(_, from_stack, cards) def getBottomImage(_): return _.game.app.images.getTalonBottom() class Braid(Game): Hint_Class = Braid_Hint BRAID_CARDS = 20 RANKS = RANKS def createGame(_): (l, s) = (Layout(_), _.s) h = max(4 * l.YS + 30, l.YS + (_.BRAID_CARDS - 1) * l.YOFFSET) _.setSize(10 * l.XS + l.XM, l.YM + h) _.base_card = None s.addattr(braid = None) (x, y) = (l.XM, l.YM) for i in range(2): s.rows.append(Braid_RowStack(x + 0.5 * l.XS, y, _)) s.rows.append(Braid_RowStack(x + 4.5 * l.XS, y, _)) y = y + 3 * l.YS y = l.YM + l.YS for i in range(2): s.rows.append(Braid_ReserveStack(x, y, _)) s.rows.append(Braid_ReserveStack(x + l.XS, y, _)) s.rows.append(Braid_ReserveStack(x, y + l.YS, _)) s.rows.append(Braid_ReserveStack(x + l.XS, y + l.YS, _)) x = x + 4 * l.XS (x, y) = (l.XM + l.XS * 5 / 2, l.YM) s.braid = Braid_BraidStack(x, y, _) (x, y) = (l.XM + 7 * l.XS, l.YM + l.YS * 3 / 2) s.talon = WasteTalonStack(x, y, _, max_rounds = 3) l.createText(s.talon, 'ss') s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's') x = x - l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') x = l.XM + 8 * l.XS y = l.YM for i in range(4): s.foundations.append(Braid_Foundation(x, y, _, i)) s.foundations.append(Braid_Foundation(x + l.XS, y, _, i)) y = y + l.YS _.texts.info = MfxCanvasText(_.canvas, x + l.CW + l.XM / 2, y, anchor = 'n', font = getFont('canvas_card', cardw = l.CW)) _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.openstacks = s.foundations + s.rows _.sg.dropstacks = [ s.braid] + s.rows + [ s.waste] def _shuffleHook(_, cards): n = m = -1 - _.BRAID_CARDS - len(_.s.rows) while cards[n].suit >= len(_.gameinfo.suits): n = n - 1 (cards[n], cards[m]) = (cards[m], cards[n]) return cards def startGame(_): _.base_card = None _.updateText() _.startDealSample() for i in range(_.BRAID_CARDS): _.s.talon.dealRow(rows = [ _.s.braid], frames = 4) _.s.talon.dealRow(frames = 4) _.base_card = _.s.talon.cards[-1] to_stack = _.s.foundations[2 * _.base_card.suit] _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, to_stack) _.updateText() for s in _.s.foundations: s.cap.base_rank = _.base_card.rank _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def getHighlightPilesStacks(_): return () def _restoreGameHook(_, game): _.base_card = _.cards[game.loadinfo.base_card_id] for s in _.s.foundations: s.cap.base_rank = _.base_card.rank def _loadGameHook(_, p): _.loadinfo.addattr(base_card_id = None) _.loadinfo.base_card_id = p.load() def _saveGameHook(_, p): p.dump(_.base_card.id) def updateText(_): if _.preview > 1 or not (_.texts.info): return None if not (_.base_card): t = '' else: t = _.RANKS[_.base_card.rank] dir = _.getFoundationDir() if dir == 1: t = t + ' Ascending' elif dir == -1: t = t + ' Descending' _.texts.info.config(text = t) class LongBraid(Braid): BRAID_CARDS = 24 registerGame(GameInfo(12, Braid, 'Braid', GI.GT_NAPOLEON, 2, 2)) registerGame(GameInfo(175, LongBraid, 'Long Braid', GI.GT_NAPOLEON, 2, 2)) class Spider_Hint(SpiderType_Hint): BONUS_SAME_SUIT_MOVE = 400 def _preferHighRankMoves(_): return 1 def shallMovePile(_, r, t, pile, rpile): if not SpiderType_Hint.shallMovePile(_, r, t, pile, rpile): return 0 rr = _.ClonedStack(r, stackcards = rpile) if rr.acceptsCards(t, pile): if len(t.cards) == 0: return 1 if pile[0].suit == t.cards[-1].suit: if len(rpile) == 0 or pile[0].suit != rpile[-1].suit: return 1 if _.level <= 1 and len(rpile) == 0: return 1 return 0 return 1 class Spider_SS_Foundation(AbstractFoundationStack): def __init__(_, x, y, game, suit = ANY_SUIT, **cap): kwdefault(cap, dir = -1, base_rank = KING, min_accept = 13, max_accept = 13, max_move = 0) apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap) def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 return isSameSuitSequence(cards, _.cap.mod, _.cap.dir) class Spider_AC_Foundation(Spider_SS_Foundation): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 return isAlternateColorSequence(cards, _.cap.mod, _.cap.dir) class Spider_RowStack(Spider_SS_RowStack): def canDropCards(_, stacks): if len(_.cards) < 13: return (None, 0) cards = _.cards[-13:] for s in stacks: pass return (None, 0) class RelaxedSpider(Game): Layout_Method = Layout.klondikeLayout Talon_Class = DealRowTalonStack Foundation_Class = Spider_SS_Foundation RowStack_Class = Spider_RowStack Hint_Class = Spider_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 10, waste = 0, texts = 1, playcards = 23) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) if l.s.waste: s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = ANY_SUIT)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() def startGame(_): for i in range(4): _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() r = _.s.rows rows = (r[0], r[3], r[6], r[9]) _.s.talon.dealRow(rows = rows, flip = 0) _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not (card1.rank + 1) % stack1.cap.mod == card2.rank: pass return (card2.rank + 1) % stack1.cap.mod == card1.rank class Spider(RelaxedSpider): def canDealCards(_): if not RelaxedSpider.canDealCards(_): return 0 for r in _.s.rows: pass return 1 class BlackWidow_RowStack(RK_RowStack, Spider_RowStack): def canDropCards(_, stacks): return Spider_RowStack.canDropCards(_, stacks) class BlackWidow(Spider): RowStack_Class = BlackWidow_RowStack class GroundForADivorce_Talon(TalonStack): def dealCards(_, sound = 1): if _.cards: rows = filter((lambda r: r.cards), _.game.s.rows) if not rows: rows = _.game.s.rows[:1] return _.dealRowAvail(rows = rows, sound = sound) return 0 class GroundForADivorce(RelaxedSpider): Layout_Method = Layout.harpLayout Talon_Class = GroundForADivorce_Talon Foundation_Class = StackWrapper(Spider_SS_Foundation, base_rank = ANY_RANK, mod = 13) RowStack_Class = StackWrapper(Spider_RowStack, mod = 13) def createGame(_): Spider.createGame(_, playcards = 22) def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() class GrandmothersGame(RelaxedSpider): Layout_Method = Layout.harpLayout def createGame(_): Spider.createGame(_, playcards = 22) def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() class Spiderette(Spider): def createGame(_): Spider.createGame(_, rows = 7, playcards = 20) def startGame(_): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class BabySpiderette(Spiderette): RowStack_Class = BlackWidow_RowStack class WillOTheWisp(Spiderette): def startGame(_): for i in range(2): _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class SimpleSimon(Spider): Talon_Class = InitialDealTalonStack def createGame(_): Spider.createGame(_, rows = 10, texts = 0) def startGame(_): for l in (9, 8, 7, 6, 5, 4, 3): _.s.talon.dealRow(rows = _.s.rows[:l], frames = 0) _.startDealSample() _.s.talon.dealRow() class Rachel(RelaxedSpider): Talon_Class = StackWrapper(WasteTalonStack, max_rounds = 1) RowStack_Class = RK_RowStack def createGame(_): Spider.createGame(_, waste = 1, rows = 6, texts = 1) def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() class Scorpion_RowStack(Yukon_SS_RowStack, Spider_RowStack): canDropCards = Spider_RowStack.canDropCards class Scorpion(RelaxedSpider): RowStack_Class = StackWrapper(Scorpion_RowStack, base_rank = KING) def createGame(_): Spider.createGame(_, rows = 7, playcards = 20) def startGame(_): for i in (4, 4, 4, 0, 0, 0): _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0) _.s.talon.dealRow(rows = _.s.rows[i:], flip = 1, frames = 0) _.startDealSample() _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () class Wasp(Scorpion): RowStack_Class = Scorpion_RowStack def startGame(_): for i in (3, 3, 3, 0, 0, 0): _.s.talon.dealRow(rows = _.s.rows[:i], flip = 0, frames = 0) _.s.talon.dealRow(rows = _.s.rows[i:], flip = 1, frames = 0) _.startDealSample() _.s.talon.dealRow() class RougeEtNoir_RowStack(KingAC_RowStack): def canDropCards(_, stacks): if not (_.cards): return (None, 0) for s in stacks: for cards in (_.cards[-1:], _.cards[-13:]): pass return (None, 0) class RougeEtNoir(Game): Layout_Method = Layout.klondikeLayout Talon_Class = DealRowTalonStack RowStack_Class = RougeEtNoir_RowStack def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 10, waste = 0, texts = 1, playcards = 23) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) if l.s.waste: s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for i in range(4): r = l.s.foundations[i] s.foundations.append(AC_FoundationStack(r.x, r.y, _, suit = i, max_move = 0)) for i in range(4): r = l.s.foundations[i + 4] s.foundations.append(Spider_AC_Foundation(r.x, r.y, _)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() return l def startGame(_, flip = 0, reverse = 1): for i in range(3, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[:-i], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[:-1], reverse = reverse) registerGame(GameInfo(10, RelaxedSpider, 'Relaxed Spider', GI.GT_SPIDER | GI.GT_RELAXED, 2, 0)) registerGame(GameInfo(11, Spider, 'Spider', GI.GT_SPIDER, 2, 0)) registerGame(GameInfo(49, BlackWidow, 'Black Widow', GI.GT_SPIDER, 2, 0)) registerGame(GameInfo(14, GroundForADivorce, 'Ground for a Divorce', GI.GT_SPIDER, 2, 0)) registerGame(GameInfo(114, GrandmothersGame, "Grandmother's Game", GI.GT_SPIDER, 2, 0)) registerGame(GameInfo(24, Spiderette, 'Spiderette', GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(47, BabySpiderette, 'Baby Spiderette', GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(48, WillOTheWisp, "Will o' the Wisp", GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(50, SimpleSimon, 'Simple Simon', GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(29, Scorpion, 'Scorpion', GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(185, Wasp, 'Wasp', GI.GT_SPIDER, 1, 0)) registerGame(GameInfo(220, RougeEtNoir, 'Rouge et Noir', GI.GT_GYPSY, 2, 0)) class FreeCell_RowStack(AC_RowStack): def _getMaxMove(_, to_stack_ncards): max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1 n = getNumberOfFreeStacks(_.game.s.rows) if to_stack_ncards == 0: n = n - 1 while n > 0 and max_move < 1000: max_move = max_move * 2 n = n - 1 return max_move def canMoveCards(_, cards): max_move = _._getMaxMove(1) if len(cards) <= max_move: pass return AC_RowStack.canMoveCards(_, cards) def acceptsCards(_, from_stack, cards): max_move = _._getMaxMove(len(_.cards)) if len(cards) <= max_move: pass return AC_RowStack.acceptsCards(_, from_stack, cards) class FreeCell(Game): Layout_Method = Layout.freeCellLayout Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = FreeCell_RowStack Hint_Class = FreeCellType_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 8, reserves = 4, texts = 0) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) for r in l.s.reserves: s.reserves.append(ReserveStack(r.x, r.y, _)) l.defaultAll() def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() r = _.s.rows _.s.talon.dealRow(rows = r[:4]) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class RelaxedFreeCell(FreeCell): RowStack_Class = AC_RowStack class ForeCell(FreeCell): RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank = KING) def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.reserves) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class Stalactites(FreeCell): Foundation_Class = StackWrapper(RK_FoundationStack, suit = ANY_SUIT, mod = 13, min_cards = 1) RowStack_Class = StackWrapper(BasicRowStack, max_move = 1, max_accept = 0) def createGame(_): FreeCell.createGame(_, reserves = 2) def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 _._restoreGameHook(None) def _restoreGameHook(_, game): for s in _.s.foundations: s.cap.base_rank = s.cards[0].rank registerGame(GameInfo(5, RelaxedFreeCell, 'Relaxed FreeCell', GI.GT_FREECELL | GI.GT_RELAXED, 1, 0)) registerGame(GameInfo(8, FreeCell, 'FreeCell', GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(46, ForeCell, 'ForeCell', GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(77, Stalactites, 'Stalactites', GI.GT_FREECELL, 1, 0)) class BakersGame_RowStack(SS_RowStack): def _getMaxMove(_, to_stack_ncards): max_move = getNumberOfFreeStacks(_.game.s.reserves) + 1 n = getNumberOfFreeStacks(_.game.s.rows) if to_stack_ncards == 0: n = n - 1 while n > 0 and max_move < 1000: max_move = max_move * 2 n = n - 1 return max_move def canMoveCards(_, cards): max_move = _._getMaxMove(1) if len(cards) <= max_move: pass return SS_RowStack.canMoveCards(_, cards) def acceptsCards(_, from_stack, cards): max_move = _._getMaxMove(len(_.cards)) if len(cards) <= max_move: pass return SS_RowStack.acceptsCards(_, from_stack, cards) class BakersGame(Game): Layout_Method = Layout.freeCellLayout RowStack_Class = BakersGame_RowStack Hint_Class = FreeCellType_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 8, reserves = 4, texts = 0) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = InitialDealTalonStack(l.s.talon.x, l.s.talon.y, _) for r in l.s.foundations: _.s.foundations.append(SS_FoundationStack(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) for r in l.s.reserves: _.s.reserves.append(ReserveStack(r.x, r.y, _)) l.defaultAll() def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() r = _.s.rows _.s.talon.dealRow(rows = r[:4]) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class KingOnlyBakersGame(BakersGame): RowStack_Class = StackWrapper(FreeCell_SS_RowStack, base_rank = KING) class EightOff(KingOnlyBakersGame): def createGame(_, rows = 8, reserves = 8): (l, s) = (Layout(_), _.s) h = max(2 * l.YS, l.YS + (16 - 1) * l.YOFFSET) maxrows = max(rows, reserves) _.setSize(l.XM + maxrows * l.XS, l.YM + l.YS + h + l.YS) (x, y) = (l.XM + (maxrows - 4) * l.XS / 2, l.YM) for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i)) x = x + l.XS (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, y + l.YS) for i in range(rows): s.rows.append(_.RowStack_Class(x, y, _)) x = x + l.XS (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, _.height - l.YS) for i in range(reserves): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS _.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999)) s.talon = InitialDealTalonStack(l.XM, l.YM, _) l.defaultStackGroups() def startGame(_): for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() r = _.s.reserves _.s.talon.dealRow(rows = [ r[0], r[2], r[4], r[6]]) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class SeahavenTowers(KingOnlyBakersGame): def createGame(_): (l, s) = (Layout(_), _.s) h = max(3 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + 10 * l.XS, l.YM + l.YS + h) (x, y) = (l.XM, l.YM) for i in range(4): s.reserves.append(ReserveStack(x + (i + 3) * l.XS, y, _)) for suit in range(4): i = (9, 0, 1, 8)[suit] s.foundations.append(SS_FoundationStack(x + i * l.XS, y, _, suit)) (x, y) = (l.XM, l.YM + l.YS) for i in range(10): s.rows.append(_.RowStack_Class(x, y, _)) x = x + l.XS _.setRegion(s.rows, (-999, y - l.YM / 2, 999999, 999999)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) _.sg.openstacks = s.foundations + s.rows + s.reserves _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows + s.reserves _.sg.reservestacks = s.reserves def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.reserves[1:3]) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class RelaxedSeahavenTowers(SeahavenTowers): RowStack_Class = KingSS_RowStack class Penguin(Game): GAME_VERSION = 2 RowStack_Class = SS_RowStack Hint_Class = FreeCellType_Hint def createGame(_, rows = 7, reserves = 7): (l, s) = (Layout(_), _.s) h = max(3 * l.YS, l.YS + (16 - 1) * l.YOFFSET) maxrows = max(rows, reserves) _.setSize(l.XM + (maxrows + 1) * l.XS, l.YM + h + l.YS) _.base_card = None (x, y) = (_.width - l.XS, l.YM) for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 13, max_move = 0)) y = y + l.YS _.setRegion(s.foundations, (x - l.CW / 2, -999, 999999, 999999)) (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, l.YM) for i in range(rows): s.rows.append(_.RowStack_Class(x, y, _, mod = 13)) x = x + l.XS (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, _.height - l.YS) for i in range(reserves): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS _.setRegion(s.reserves, (-999, y - l.CH / 2, 999999, 999999)) s.talon = InitialDealTalonStack(l.XM + 1, y, _) l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c, rank = cards[-1].rank: (c.rank == rank, 0))) def startGame(_): _.base_card = _.s.talon.cards[-4] _._updateStacks() for i in range(3): c = _.s.talon.getCard() if not __debug__ and c.rank == _.base_card.rank: raise AssertionError 0 to_stack = _.s.foundations[c.suit * _.gameinfo.decks] _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, to_stack, frames = 0) for i in range(6): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def _restoreGameHook(_, game): _.base_card = _.cards[game.loadinfo.base_card_id] _._updateStacks() def _loadGameHook(_, p): _.loadinfo.addattr(base_card_id = None) _.loadinfo.base_card_id = p.load() def _saveGameHook(_, p): p.dump(_.base_card.id) def _updateStacks(_): for s in _.s.foundations: s.cap.base_rank = _.base_card.rank for s in _.s.rows: s.cap.base_rank = (_.base_card.rank - 1) % 13 registerGame(GameInfo(45, BakersGame, "Baker's Game", GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(26, KingOnlyBakersGame, "King Only Baker's Game", GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(258, EightOff, 'Eight Off', GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(9, SeahavenTowers, 'Seahaven Towers', GI.GT_FREECELL, 1, 0)) registerGame(GameInfo(6, RelaxedSeahavenTowers, 'Relaxed Seahaven Towers', GI.GT_FREECELL | GI.GT_RELAXED, 1, 0)) registerGame(GameInfo(64, Penguin, 'Penguin', GI.GT_FREECELL, 1, 0)) class EiffelTower_RowStack(OpenStack): def __init__(_, x, y, game): OpenStack.__init__(_, x, y, game, max_move = 0, max_accept = 1) _.CARD_YOFFSET = 1 def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 return _.cards[-1].rank + cards[0].rank == 12 class EiffelTower(Game): Talon_Class = WasteTalonStack Waste_Class = WasteStack def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8.5 * l.XS, l.YM + 6 * l.YS) y = l.YM for d in ((1, 2.5), (2, 2), (3, 1.5), (4, 1), (5, 0.5), (5, 0.5)): x = l.XM + d[1] * l.XS for i in range(d[0]): s.rows.append(EiffelTower_RowStack(x, y, _)) x = x + l.XS y = y + l.YS x = l.XM + 6 * l.XS y = l.YM + 5 * l.YS / 2 s.waste = _.Waste_Class(x, y, _) l.createText(s.waste, 'ss') x = x + l.XS s.talon = _.Talon_Class(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def isGameWon(_): if len(_.s.talon.cards) == 0: pass return len(_.s.waste.cards) == 0 def getAutoStacks(_, event = None): return ((), (), ()) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank + card2.rank == 12 class StrictEiffelTower(EiffelTower): Waste_Class = StackWrapper(WasteStack, max_cards = 2) registerGame(GameInfo(16, EiffelTower, 'Eiffel Tower', GI.GT_PAIRING_TYPE, 2, 0)) class Matriarchy_Waste(WasteStack): def updateText(_): WasteStack.updateText(_) if _.game.s.talon._updateMaxRounds(): _.game.s.talon.updateText() class Matriarchy_Talon(WasteTalonStack): DEAL = (2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 11, 10, 9, 8, 7, 6, 5) def _updateMaxRounds(_): old = _.max_rounds _.max_rounds = 11 rows = _.game.s.rows for i in (0, 2, 4, 6): l1 = len(rows[i + 0].cards) + len(rows[i + 8].cards) l2 = len(rows[i + 1].cards) + len(rows[i + 9].cards) if not __debug__ and l1 + l2 <= 26: raise AssertionError 0 if l1 + l2 == 26: _.max_rounds = _.max_rounds + 2 elif l1 >= 13 or l2 >= 13: _.max_rounds = _.max_rounds + 1 if _.max_rounds == 19: _.max_rounds = 18 return old != _.max_rounds def canDealCards(_): if _._updateMaxRounds(): _.updateText() if not (_.cards) and not (_.game.s.waste.cards): return 0 ncards = _.DEAL[_.round - 1] if not __debug__ and ncards > 0: raise AssertionError if not len(_.cards) >= ncards: pass return _.round < _.max_rounds def dealCards(_, sound = 0): ncards = _.DEAL[_.round - 1] if not __debug__ and ncards > 0: raise AssertionError waste = _.game.s.waste n = 0 update_flags = 1 while n < ncards: while n < ncards: card = _.getCard() if not card: break if not __debug__ and not (card.face_up): raise AssertionError _.game.flipMove(_) _.game.moveMove(1, _, waste, frames = 3, shadow = 0) n = n + 1 if n < ncards and len(waste.cards) > 0: if not __debug__ and len(_.cards) == 0: raise AssertionError if __debug__: if not _.round < _.max_rounds or update_flags == 0: raise AssertionError _.game.turnStackMove(waste, _, update_flags = update_flags) update_flags = 0 if not __debug__ and _.round <= _.max_rounds: raise AssertionError if not __debug__ and n == ncards: raise AssertionError if not __debug__ and len(_.game.s.waste.cards) > 0: raise AssertionError return n def updateText(_): if _.game.preview > 1: return None WasteTalonStack.updateText(_, update_rounds = 0) t = 'Round %d/%d' % (_.round, _.max_rounds) _.texts.rounds.config(text = t) t = 'Deal %d' % _.DEAL[_.round - 1] _.texts.misc.config(text = t) class Matriarchy_UpRowStack(SS_RowStack): def __init__(_, x, y, game, suit): SS_RowStack.__init__(_, x, y, game, suit = suit, base_rank = KING, mod = 13, dir = 1, min_cards = 1, max_cards = 12) _.CARD_YOFFSET = -(_.CARD_YOFFSET) def getBottomImage(_): return _.game.app.images.getSuitBottom(_.cap.suit) class Matriarchy_DownRowStack(SS_RowStack): def __init__(_, x, y, game, suit): SS_RowStack.__init__(_, x, y, game, suit = suit, base_rank = QUEEN, mod = 13, dir = -1, min_cards = 1, max_cards = 12) def getBottomImage(_): return _.game.app.images.getSuitBottom(_.cap.suit) class Matriarchy(Game): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_), _.s) h = max(2 * l.YS, (12 - 1) * l.YOFFSET + l.CH * 2 / 3) _.setSize(10 * l.XS + l.XM, h + l.YM + h) (center, c1, c2) = (_.height / 2, h, _.height - h) (x, y) = (l.XM, c1 - l.CH) for i in range(8): s.rows.append(Matriarchy_UpRowStack(x, y, _, i / 2)) x = x + l.XS (x, y) = (l.XM, c2) for i in range(8): s.rows.append(Matriarchy_DownRowStack(x, y, _, i / 2)) x = x + l.XS (x, y) = (x + l.XS / 2, c1 - l.CH / 2 - l.CH) tx = x + l.CW / 2 s.waste = Matriarchy_Waste(x, y, _) l.createText(s.waste, 'ss') y = c2 + l.CH / 2 s.talon = Matriarchy_Talon(x, y, _, max_rounds = VARIABLE_REDEALS) l.createText(s.talon, 'nn') s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, y + l.YS, anchor = 'n') s.talon.texts.misc = MfxCanvasText(_.canvas, tx, center, anchor = 'center', font = getFont('canvas_large')) l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 11, c.suit)), 8) def startGame(_): _.startDealSample() _.s.talon.dealRow(_.s.rows[8:]) _.s.talon.dealCards() def isGameWon(_): if len(_.s.talon.cards) == 0: pass return len(_.s.waste.cards) == 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank + card2.rank == QUEEN + KING: return 0 if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank registerGame(GameInfo(17, Matriarchy, 'Matriarchy', GI.GT_2DECK_TYPE, 2, VARIABLE_REDEALS)) class Calculation_Hint(DefaultHint): def _getMoveWasteScore(_, score, color, r, t, pile, rpile): if __debug__: if not r is _.game.s.waste and len(pile) == 1: raise AssertionError score = 30000 if len(t.cards) == 0: score = score - (KING - r.cards[0].rank) * 1000 elif t.cards[-1].rank < r.cards[0].rank: score = 10000 + t.cards[-1].rank - len(t.cards) elif t.cards[-1].rank == r.cards[0].rank: score = 20000 else: score = score - (t.cards[-1].rank - r.cards[0].rank) * 1000 return (score, color) class BetsyRoss_Foundation(RK_FoundationStack): def updateText(_): if _.game.preview > 1: return None if _.texts.misc: if len(_.cards) == 0: rank = _.cap.base_rank _.texts.misc.config(text = RANKS[rank]) elif len(_.cards) == _.cap.max_cards: _.texts.misc.config(text = '') else: rank = (_.cards[-1].rank + _.cap.dir) % _.cap.mod _.texts.misc.config(text = RANKS[rank]) class Calculation_Foundation(BetsyRoss_Foundation): def getBottomImage(_): return _.game.app.images.getLetter(_.cap.base_rank) class Calculation_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if from_stack is _.game.s.waste: pass return len(cards) == 1 def getBottomImage(_): return _.game.app.images.getReserveBottom() class Calculation(Game): Hint_Class = Calculation_Hint def createGame(_): (l, s) = (Layout(_), _.s) h = max(2 * l.YS, 20 * l.YOFFSET) _.setSize(5.5 * l.XS + l.XM, l.YM + l.YS + 30 + h) x0 = l.XM + l.XS * 3 / 2 (x, y) = (x0, l.YM) for i in range(4): stack = Calculation_Foundation(x, y, _, base_rank = i, mod = 13, dir = i + 1) s.foundations.append(stack) stack.texts.misc = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS, anchor = 'n', font = getFont('canvas_card', cardw = l.CW)) x = x + l.XS help = '1: A 2 3 4 5 6 7 8 9 T J Q K\n' + '2: 2 4 6 8 T Q A 3 5 7 9 J K\n' + '3: 3 6 9 Q 2 5 8 J A 4 7 T K\n' + '4: 4 8 Q 3 7 J 2 6 T A 5 9 K' _.texts.help = MfxCanvasText(_.canvas, x + l.XM, y + l.CH / 2, text = help, anchor = 'w', font = getFont('canvas_fixed')) x = x0 y = l.YM + l.YS + 30 for i in range(4): s.rows.append(Calculation_RowStack(x, y, _, max_move = 1, max_accept = 1)) x = x + l.XS _.setRegion(s.rows, (-999, y, 999999, 999999)) x = l.XM s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'nn') y = y + l.YS s.waste = WasteStack(x, y, _, max_cards = 1) l.defaultStackGroups() def _shuffleHook(_, cards): topcards = [ None] * 4 for c in cards[:]: pass topcards.reverse() return cards + topcards def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) _.s.talon.dealCards() def getHighlightPilesStacks(_): return () class Hopscotch(Calculation): def _shuffleHook(_, cards): topcards = [ None] * 4 for c in cards[:]: pass topcards.reverse() return cards + topcards class BetsyRoss(Calculation): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(5.5 * l.XS + l.XM, l.YM + l.YS + 30 + 3 * l.YS) x0 = l.XM + l.XS * 3 / 2 (x, y) = (x0, l.YM) for i in range(4): stack = BetsyRoss_Foundation(x, y, _, base_rank = i, max_cards = 1, max_move = 0, max_accept = 0) s.foundations.append(stack) x = x + l.XS x = x0 y = l.YM + l.YS + 30 for i in range(4): stack = BetsyRoss_Foundation(x, y, _, base_rank = 2 * i + 1, mod = 13, dir = i + 1, max_cards = 12, max_move = 0) stack.texts.misc = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's', font = getFont('canvas_card', cardw = l.CW)) s.foundations.append(stack) x = x + l.XS help = '1: 2 3 4 5 6 7 8 9 T J Q K\n' + '2: 4 6 8 T Q A 3 5 7 9 J K\n' + '3: 6 9 Q 2 5 8 J A 4 7 T K\n' + '4: 8 Q 3 7 J 2 6 T A 5 9 K' _.texts.help = MfxCanvasText(_.canvas, x + l.XM, y + l.CH / 2, text = help, anchor = 'w', font = getFont('canvas_fixed')) x = l.XM s.talon = WasteTalonStack(x, y, _, max_rounds = 3) l.createText(s.talon, 'nn') y = y + l.YS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() def _shuffleHook(_, cards): topcards = [ None] * 8 for c in cards[:]: if c.rank <= 3 and topcards[c.rank] is None: topcards[c.rank] = c cards.remove(c) elif c.rank in (1, 3, 5, 7): i = 4 + (c.rank - 1) / 2 if topcards[i] is None: topcards[i] = c cards.remove(c) topcards.reverse() return cards + topcards registerGame(GameInfo(256, Calculation, 'Calculation', GI.GT_1DECK_TYPE, 1, 0)) registerGame(GameInfo(94, Hopscotch, 'Hopscotch', GI.GT_1DECK_TYPE, 1, 0)) registerGame(GameInfo(134, BetsyRoss, 'Betsy Ross', GI.GT_1DECK_TYPE, 1, 2)) class Canfield_Hint(CautiousDefaultHint): def _getMoveWasteScore(_, score, color, r, t, pile, rpile): (score, color) = CautiousDefaultHint._getMovePileScore(_, score, color, r, t, pile, rpile) return (score + 100000, color) class Canfield_AC_RowStack(AC_RowStack): def basicAcceptsCards(_, from_stack, cards): if from_stack in _.game.s.rows: if len(cards) != 1 and len(cards) != len(from_stack.cards): return 0 return AC_RowStack.basicAcceptsCards(_, from_stack, cards) class Canfield_SS_RowStack(SS_RowStack): def basicAcceptsCards(_, from_stack, cards): if from_stack in _.game.s.rows: if len(cards) != 1 and len(cards) != len(from_stack.cards): return 0 return SS_RowStack.basicAcceptsCards(_, from_stack, cards) class Canfield_RK_RowStack(RK_RowStack): def basicAcceptsCards(_, from_stack, cards): if from_stack in _.game.s.rows: if len(cards) != 1 and len(cards) != len(from_stack.cards): return 0 return RK_RowStack.basicAcceptsCards(_, from_stack, cards) class Canfield(Game): Foundation_Class = SS_FoundationStack RowStack_Class = StackWrapper(Canfield_AC_RowStack, mod = 13) ReserveStack_Class = OpenStack Hint_Class = Canfield_Hint INITIAL_RESERVE_CARDS = 13 INITIAL_RESERVE_FACEUP = 0 FILL_EMPTY_ROWS = 1 def createGame(_, rows = 4, max_rounds = -1, num_deal = 3): (l, s) = (Layout(_), _.s) decks = _.gameinfo.decks h = max(3 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + (2 + max(rows, 4 * decks)) * l.XS + l.XM, l.YM + l.YS + 20 + h) _.base_card = None (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal) l.createText(s.talon, 's') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 's') x = x + l.XM for i in range(4): for j in range(decks): x = x + l.XS s.foundations.append(_.Foundation_Class(x, y, _, i, mod = 13, max_move = 0)) font = getFont('canvas_card', cardw = l.CW) _.texts.info = MfxCanvasText(_.canvas, tx, ty, anchor = ta, font = font) (x, y) = (l.XM, l.YM + l.YS + 20) s.reserves.append(_.ReserveStack_Class(x, y, _)) s.reserves[0].CARD_YOFFSET = 10 x = l.XM + 2 * l.XS + l.XM for i in range(rows): s.rows.append(_.RowStack_Class(x, y, _)) x = x + l.XS l.defaultStackGroups() def updateText(_): if _.preview > 1: return None if not (_.base_card): t = '' else: t = RANKS[_.base_card.rank] _.texts.info.config(text = t) def startGame(_): _.startDealSample() _.base_card = None _.updateText() _.base_card = _.s.talon.getCard() for s in _.s.foundations: s.cap.base_rank = _.base_card.rank n = _.base_card.suit * _.gameinfo.decks if _.s.foundations[n].cards: if not __debug__ and _.gameinfo.decks > 1: raise AssertionError 0 n = n + 1 _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, _.s.foundations[n]) _.updateText() for i in range(_.INITIAL_RESERVE_CARDS): _.moveMove(1, _.s.talon, _.s.reserves[0], frames = 4, shadow = 0) if _.s.reserves[0].canFlipCard(): _.flipMove(_.s.reserves[0]) _.s.talon.dealRow(reverse = 1) _.s.talon.dealCards() def fillStack(_, stack): if stack in _.s.rows and _.s.reserves: if _.FILL_EMPTY_ROWS: if not (stack.cards) and _.s.reserves[0].cards: if not (_.s.reserves[0].cards[-1].face_up): _.s.reserves[0].flipMove() _.s.reserves[0].moveMove(1, stack) elif stack in _.s.reserves: if stack.canFlipCard(): stack.flipMove() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def _restoreGameHook(_, game): _.base_card = _.cards[game.loadinfo.base_card_id] for s in _.s.foundations: s.cap.base_rank = _.base_card.rank def _loadGameHook(_, p): _.loadinfo.addattr(base_card_id = None) _.loadinfo.base_card_id = p.load() def _saveGameHook(_, p): p.dump(_.base_card.id) class SuperiorCanfield(Canfield): INITIAL_RESERVE_FACEUP = 1 FILL_EMPTY_ROWS = 0 class Rainfall(Canfield): def createGame(_): Canfield.createGame(_, max_rounds = 3, num_deal = 1) class Rainbow(Canfield): RowStack_Class = StackWrapper(Canfield_RK_RowStack, mod = 13) def createGame(_): Canfield.createGame(_, max_rounds = 1, num_deal = 1) class Storehouse(Canfield): RowStack_Class = StackWrapper(Canfield_SS_RowStack, mod = 13) def createGame(_): Canfield.createGame(_, max_rounds = 3, num_deal = 1) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 1, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations[:3]) Canfield.startGame(_) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def updateText(_): pass class Chameleon(Canfield): RowStack_Class = StackWrapper(Canfield_RK_RowStack, mod = 13) INITIAL_RESERVE_CARDS = 12 def createGame(_): Canfield.createGame(_, rows = 3, max_rounds = 1, num_deal = 1) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank class DoubleCanfield(Canfield): def createGame(_): Canfield.createGame(_, rows = 5) class AmericanToad(Canfield): RowStack_Class = StackWrapper(Canfield_SS_RowStack, mod = 13) INITIAL_RESERVE_CARDS = 20 INITIAL_RESERVE_FACEUP = 1 def createGame(_): Canfield.createGame(_, rows = 8, max_rounds = 2, num_deal = 1) class VariegatedCanfield(Canfield): RowStack_Class = Canfield_AC_RowStack INITIAL_RESERVE_FACEUP = 1 def createGame(_): Canfield.createGame(_, rows = 5, max_rounds = 3) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations[:7]) Canfield.startGame(_) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def updateText(_): pass class EagleWing_ReserveStack(OpenStack): def canFlipCard(_): if len(_.cards) == 1: pass return not (_.cards[-1].face_up) class EagleWing(Canfield): RowStack_Class = StackWrapper(SS_RowStack, mod = 13, max_move = 1, max_cards = 3) ReserveStack_Class = EagleWing_ReserveStack def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 9 * l.XS + l.XM, l.YM + 4 * l.YS) _.base_card = None (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 3, num_deal = 1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') for i in range(4): x = l.XM + (i + 3) * l.XS s.foundations.append(_.Foundation_Class(x, y, _, i, mod = 13, max_move = 0)) (tx, ty, ta, tf) = l.getTextAttr(None, 'se') (tx, ty) = (x + tx + l.XM, y + ty) font = getFont('canvas_card', cardw = l.CW) _.texts.info = MfxCanvasText(_.canvas, tx, ty, anchor = ta, font = font) ry = l.YM + 2 * l.YS for i in range(8): x = l.XM + (i + (i >= 4)) * l.XS y = ry - (0.2, 0.4, 0.6, 0.4, 0.4, 0.6, 0.4, 0.2)[i] * l.CH s.rows.append(_.RowStack_Class(x, y, _)) (x, y) = (l.XM + 4 * l.XS, ry) s.reserves.append(_.ReserveStack_Class(x, y, _)) l.createText(s.reserves[0], 'ss') l.defaultStackGroups() registerGame(GameInfo(105, Canfield, 'Canfield', GI.GT_CANFIELD | GI.GT_CONTRIB, 1, -1)) registerGame(GameInfo(101, SuperiorCanfield, 'Superior Canfield', GI.GT_CANFIELD, 1, -1)) registerGame(GameInfo(99, Rainfall, 'Rainfall', GI.GT_CANFIELD | GI.GT_ORIGINAL, 1, 2)) registerGame(GameInfo(108, Rainbow, 'Rainbow', GI.GT_CANFIELD, 1, 0)) registerGame(GameInfo(100, Storehouse, 'Storehouse', GI.GT_CANFIELD, 1, 2, altnames = 'Straight Up')) registerGame(GameInfo(43, Chameleon, 'Chameleon', GI.GT_CANFIELD, 1, 0, altnames = 'Kansas')) registerGame(GameInfo(106, DoubleCanfield, 'Double Canfield', GI.GT_CANFIELD, 2, -1)) registerGame(GameInfo(103, AmericanToad, 'American Toad', GI.GT_CANFIELD, 2, 1)) registerGame(GameInfo(102, VariegatedCanfield, 'Variegated Canfield', GI.GT_CANFIELD, 2, 2)) registerGame(GameInfo(112, EagleWing, 'Eagle Wing', GI.GT_CANFIELD, 1, 2)) class Golf_Hint(AbstractHint): def computeHints(_): game = _.game for r in game.sg.dropstacks: (w, ncards) = r.canDropCards(game.s.foundations) if __debug__: if not w is game.s.waste and ncards == 1: raise AssertionError None if not w else game.sg.dropstacks ww = (_.ClonedStack(w, stackcards = w.cards + [ r.cards[-1]]),) (score, color) = (10000 + r.id, None) for t in game.sg.dropstacks: if t is r: t = _.ClonedStack(r, stackcards = r.cards[:-1]) if t.canFlipCard(): score = score + 100 elif t.canDropCards(ww)[0]: score = score + 100 _.addHint(score, ncards, r, w, color) class Golf_RowStack(BasicRowStack): def clickHandler(_, event): return _.doubleclickHandler(event) class Golf_Waste(WasteStack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 0, max_accept = 1) apply(WasteStack.__init__, (_, x, y, game), cap) def acceptsCards(_, from_stack, cards): if not WasteStack.acceptsCards(_, from_stack, cards): return 0 (r1, r2) = (_.cards[-1].rank, cards[0].rank) if _.game.getStrictness() == 1: if r1 == KING: return 0 if not (r1 + 1) % _.cap.mod == r2: pass return (r2 + 1) % _.cap.mod == r1 class Golf(Game): Waste_Class = Golf_Waste Hint_Class = Golf_Hint def createGame(_): (l, s) = (Layout(_, XOFFSET = 10), _.s) (w1, w2) = (8 * l.XS + l.XM, 2 * l.XS) if w2 + 52 * l.XOFFSET > w1: l.XOFFSET = int((w1 - w2) / 52) _.setSize(w1, 4 * l.YS + l.YM) (x, y) = (l.XM + l.XS / 2, l.YM) for i in range(7): s.rows.append(Golf_RowStack(x, y, _)) x = x + l.XS (x, y) = (l.XM, _.height - l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'nn') x = x + l.XS s.waste = _.Waste_Class(x, y, _) s.waste.CARD_XOFFSET = l.XOFFSET l.createText(s.waste, 'nn') s.foundations.append(s.waste) _.sg.openstacks = [ s.waste] _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () def getAutoStacks(_, event = None): if event is None: return (_.sg.dropstacks, (), ()) else: return (_.sg.dropstacks, _.sg.dropstacks, ()) class DeadKingGolf(Golf): def getStrictness(_): return 1 def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank == KING: return 0 return Golf.shallHighlightMatch(_, stack1, card1, stack2, card2) class RelaxedGolf(Golf): Waste_Class = StackWrapper(Golf_Waste, mod = 13) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank class Elevator_RowStack(Golf_RowStack): STEP = (1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6) def basicIsBlocked(_): (r, step) = (_.game.s.rows, _.STEP) (i, n) = (_.id, 1) while i < 21: i = i + step[i] n = n + 1 for j in range(i, i + n): pass continue None if r[j].cards else range(i, i + n) return 0 class Elevator(RelaxedGolf): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(9 * l.XS + l.XM, 4 * l.YS + l.YM) for i in range(7): x = l.XM + (8 - i) * l.XS / 2 y = l.YM + i * l.YS / 2 for j in range(i + 1): s.rows.append(Elevator_RowStack(x, y, _)) x = x + l.XS (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = _.Waste_Class(x, y, _) l.createText(s.waste, 'ss') s.foundations.append(s.waste) _.sg.openstacks = [ s.waste] _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[:21], flip = 0) _.s.talon.dealRow(rows = _.s.rows[21:]) _.s.talon.dealCards() registerGame(GameInfo(36, Golf, 'Golf', GI.GT_GOLF, 1, 0)) registerGame(GameInfo(259, DeadKingGolf, 'Dead King Golf', GI.GT_GOLF, 1, 0)) registerGame(GameInfo(260, RelaxedGolf, 'Relaxed Golf', GI.GT_GOLF | GI.GT_RELAXED, 1, 0)) registerGame(GameInfo(40, Elevator, 'Elevator', GI.GT_GOLF, 1, 0)) class GrandfathersClock_Hint(CautiousDefaultHint): def _getDropCardScore(_, score, color, r, t, ncards): return (92000, color) class GrandfathersClock(Game): Hint_Class = GrandfathersClock_Hint def createGame(_): (l, s) = (Layout(_, XOFFSET = 10), _.s) dh = max(3 * l.YS / 2 + l.CH, l.YS + (9 - 1) * l.YOFFSET) _.setSize(10 * l.XS + l.XM, l.YM + 2 * dh) for i in range(2): (x, y) = (l.XM, l.YM + i * dh) for j in range(4): s.rows.append(RK_RowStack(x, y, _, max_move = 1, max_accept = 1)) x = x + l.XS y = l.YM + dh - l.CH / 2 _.setRegion(s.rows[:4], (-999, -999, x - l.XM / 2, y)) _.setRegion(s.rows[4:], (-999, y, x - l.XM / 2, 999999)) d = [ (0, 0), (1, 0.15), (2, 0.5), (2.5, 1.5), (2, 2.5), (1, 2.85)] for i in range(len(d)): d.append((0 - d[i][0], 3 - d[i][1])) (x0, y0) = (l.XM, l.YM + dh - l.CH) for i in range(12): j = (i + 5) % 12 x = int(round(x0 + (6.5 + d[j][0]) * l.XS)) y = int(round(y0 + (-1.5 + d[j][1]) * l.YS)) suit = (1, 2, 0, 3)[i % 4] s.foundations.append(SS_FoundationStack(x, y, _, suit, base_rank = i + 1, mod = 13, max_move = 0)) s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _) _.sg.openstacks = s.foundations + s.rows _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows def _shuffleHook(_, cards): (C, S, H, D) = (0 * 13, 1 * 13, 2 * 13, 3 * 13) ids = (1 + S, 2 + H, 3 + C, 4 + D, 5 + S, 6 + H, 7 + C, 8 + D, 9 + S, 10 + H, 11 + C, 12 + D) clocks = [] for c in cards[:]: pass clocks.sort((lambda a, b: cmp(b.rank, a.rank))) return clocks + cards def startGame(_): _.playSample('grandfathersclock', loop = 1) for i in range(4): _.s.talon.dealRow(frames = 0) _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () def getAutoStacks(_, event = None): return ((), (), ()) registerGame(GameInfo(261, GrandfathersClock, "Grandfather's Clock", GI.GT_1DECK_TYPE, 1, 0)) class Numerica_Hint(DefaultHint): def _getMoveWasteScore(_, score, color, r, t, pile, rpile): if __debug__: if not r is _.game.s.waste and len(pile) == 1: raise AssertionError score = 30000 if len(t.cards) == 0: score = score - (KING - r.cards[0].rank) * 1000 elif t.cards[-1].rank < r.cards[0].rank: score = 10000 + t.cards[-1].rank - len(t.cards) elif t.cards[-1].rank == r.cards[0].rank: score = 20000 else: score = score - (t.cards[-1].rank - r.cards[0].rank) * 1000 return (score, color) class Numerica_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if from_stack is _.game.s.waste: pass return len(cards) == 1 def getBottomImage(_): return _.game.app.images.getReserveBottom() class Numerica(Game): Hint_Class = Numerica_Hint Foundation_Class = StackWrapper(RK_FoundationStack, suit = ANY_SUIT) def createGame(_, rows = 4): (l, s) = (Layout(_), _.s) h = max(2 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + (1.5 + rows) * l.XS + l.XM, l.YM + l.YS + h) x0 = l.XM + l.XS * 3 / 2 (x, y) = (x0 + (rows - 4) * l.XS / 2, l.YM) for i in range(4): s.foundations.append(_.Foundation_Class(x, y, _, suit = i)) x = x + l.XS (x, y) = (x0, l.YM + l.YS) for i in range(rows): s.rows.append(Numerica_RowStack(x, y, _, max_accept = 1)) x = x + l.XS _.setRegion(s.rows, (x0 - l.XS / 2, y, 999999, 999999)) x = l.XM s.talon = WasteTalonStack(x, y, _, max_rounds = 1) s.talon.texts.ncards = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's') y = y + l.YS s.waste = WasteStack(x, y, _, max_cards = 1) _.sg.openstacks = s.foundations + s.rows _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.dropstacks = s.rows + [ s.waste] def startGame(_): _.startDealSample() _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () class LadyBetty(Numerica): Foundation_Class = SS_FoundationStack def createGame(_): Numerica.createGame(_, rows = 6) registerGame(GameInfo(257, Numerica, 'Numerica', GI.GT_NUMERICA | GI.GT_CONTRIB, 1, 0, altnames = 'Sir Tommy')) registerGame(GameInfo(171, LadyBetty, 'Lady Betty', GI.GT_NUMERICA, 1, 0)) class Yukon_Hint(YukonType_Hint): BONUS_FLIP_CARD = 9000 BONUS_CREATE_EMPTY_ROW = 100 def _getMovePileScore(_, score, color, r, t, pile, rpile): (s, color) = YukonType_Hint._getMovePileScore(_, score, color, r, t, pile, rpile) bonus = s - score if __debug__: if bonus <= bonus: pass elif not bonus <= 9999: raise AssertionError tpile = t.getPile() return (score + bonus, color) class Yukon(Game): Layout_Method = Layout.yukonLayout Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = StackWrapper(Yukon_AC_RowStack, base_rank = KING) Hint_Class = Yukon_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 7, texts = 0) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit, max_move = 0)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() return l def startGame(_): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0) for i in range(4): _.s.talon.dealRow(rows = _.s.rows[1:], flip = 1, frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def getHighlightPilesStacks(_): return () def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class RussianSolitaire(Yukon): RowStack_Class = StackWrapper(Yukon_SS_RowStack, base_rank = KING) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Odessa(RussianSolitaire): def startGame(_): for i in range(3): _.s.talon.dealRow(flip = 0, frames = 0) for i in range(2): _.s.talon.dealRow(frames = 0) for i in range(2): _.s.talon.dealRow(rows = _.s.rows[1:6], frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class Alaska_RowStack(Yukon_SS_RowStack): def _isSequence(_, c1, c2): if not c1.suit == c2.suit and (c1.rank + _.cap.dir) % _.cap.mod == c2.rank: pass return (c2.rank + _.cap.dir) % _.cap.mod == c1.rank class Alaska(RussianSolitaire): RowStack_Class = StackWrapper(Alaska_RowStack, base_rank = KING) class ChineseDiscipline(Yukon): Layout_Method = Layout.klondikeLayout Talon_Class = DealRowTalonStack def createGame(_): return Yukon.createGame(_, waste = 0, texts = 1) def startGame(_): for i in (3, 3, 3, 4, 5, 6): _.s.talon.dealRow(rows = _.s.rows[:i], flip = 1, frames = 0) _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class ChineseSolitaire(ChineseDiscipline): RowStack_Class = Yukon_AC_RowStack class Queenie(Yukon): Layout_Method = Layout.klondikeLayout Talon_Class = DealRowTalonStack def createGame(_): return Yukon.createGame(_, waste = 0, texts = 1) def startGame(_, flip = 1, reverse = 1): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(reverse = reverse) class Rushdike(RussianSolitaire): Layout_Method = Layout.klondikeLayout Talon_Class = DealRowTalonStack def createGame(_): return RussianSolitaire.createGame(_, waste = 0, texts = 1) def startGame(_, flip = 0, reverse = 1): for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(reverse = reverse) class RussianPoint(Rushdike): def startGame(_): r = _.s.rows for i in (1, 1, 2, 2, 3, 3): _.s.talon.dealRow(rows = r[i:len(r) - i], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() class Abacus_Foundation(SS_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, base_rank = suit, mod = 13, dir = suit + 1, max_move = 0) apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap) class Abacus_RowStack(Yukon_SS_RowStack): def _isSequence(_, c1, c2): (dir, mod) = (-(c1.suit + 1), 13) if c1.suit == c2.suit: pass return (c1.rank + dir) % mod == c2.rank class Abacus(Rushdike): Foundation_Class = Abacus_Foundation RowStack_Class = Abacus_RowStack def createGame(_): l = Rushdike.createGame(_) help = 'Club: A 2 3 4 5 6 7 8 9 T J Q K\n' + 'Spade: 2 4 6 8 T Q A 3 5 7 9 J K\n' + 'Heart: 3 6 9 Q 2 5 8 J A 4 7 T K\n' + 'Diamond: 4 8 Q 3 7 J 2 6 T A 5 9 K' _.texts.help = MfxCanvasText(_.canvas, l.XM, _.height - l.YM, text = help, anchor = 'sw', font = getFont('canvas_fixed')) def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.id in (0, 14, 28, 42), c.suit))) def startGame(_, flip = 1, reverse = 1): _.s.talon.dealRow(rows = _.s.foundations, frames = 0) for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = flip, frames = 0, reverse = reverse) _.startDealSample() _.s.talon.dealRow(reverse = reverse) def shallHighlightMatch(_, stack1, card1, stack2, card2): (dir, mod) = (-(card1.suit + 1), 13) if not card1.suit == card2.suit and (card1.rank + dir) % mod == card2.rank: pass return (card2.rank + dir) % mod == card1.rank registerGame(GameInfo(19, Yukon, 'Yukon', GI.GT_YUKON, 1, 0)) registerGame(GameInfo(20, RussianSolitaire, 'Russian Solitaire', GI.GT_YUKON, 1, 0)) registerGame(GameInfo(27, Odessa, 'Odessa', GI.GT_YUKON, 1, 0)) registerGame(GameInfo(186, Alaska, 'Alaska', GI.GT_YUKON, 1, 0)) class CastlesInSpain(Game): Layout_Method = Layout.bakersDozenLayout Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = AC_RowStack Hint_Class = CautiousDefaultHint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 13, playcards = 9) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _, max_move = 1, max_accept = 1)) l.defaultAll() def startGame(_, flip = (0, 0, 0)): for f in flip: _.s.talon.dealRow(flip = f, frames = 0) _.startDealSample() _.s.talon.dealRow() class Martha_RowStack(AC_RowStack): def acceptsCards(_, from_stack, cards): if not AC_RowStack.acceptsCards(_, from_stack, cards): return 0 if not _.cards: pass return len(cards) == 1 class Martha(CastlesInSpain): RowStack_Class = FullStackWrapper(Martha_RowStack) def createGame(_): CastlesInSpain.createGame(_, rows = 12, playcards = 13) def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): CastlesInSpain.startGame(_, flip = (0, 1, 0)) _.s.talon.dealRow(rows = _.s.foundations) class BakersDozen(CastlesInSpain): RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK) def _shuffleHook(_, cards): (i, n) = (0, len(_.s.rows)) kings = [] for c in cards: i = i + 1 for i in kings: j = i % n while j < i: j = j + n continue None if c.rank == KING else cards if cards[j].rank != KING else kings cards.reverse() return cards def startGame(_): CastlesInSpain.startGame(_, flip = (1, 1, 1)) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class SpanishPatience(BakersDozen): Foundation_Class = AC_FoundationStack class GoodMeasure(BakersDozen): def createGame(_): CastlesInSpain.createGame(_, rows = 10) def _shuffleHook(_, cards): cards = BakersDozen._shuffleHook(_, cards) return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit)), 2) def startGame(_): CastlesInSpain.startGame(_, flip = (1, 1, 1, 1)) for i in range(2): c = _.s.talon.cards[-1] if not __debug__ and c.rank == ACE: raise AssertionError 0 _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, _.s.foundations[c.suit]) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError range(2) class Cruel_Talon(TalonStack): def canDealCards(_): if _.game.demo and _.game.moves.index >= 100: return 0 return not _.game.isGameWon() def dealCards(_, sound = 0): lr = len(_.game.s.rows) num_cards = 0 if not __debug__ and len(_.cards) == 0: raise AssertionError rows = list(_.game.s.rows)[:] rows.reverse() for r in rows: for i in range(len(r.cards)): num_cards = num_cards + 1 _.game.moveMove(1, r, _, frames = 0) if not __debug__ and len(_.cards) == num_cards: raise AssertionError 0 _.game.nextRoundMove(_) (n, i) = (num_cards, 0) deal = [ 4] * lr extra_cards = n - 4 * lr while extra_cards > 0: deal[i] = deal[i] + 1 i = (i + 1) % lr extra_cards = extra_cards - 1 continue rows if num_cards == 0 else 0 for i in range(lr): k = min(deal[i], n) frames = (0, 4)[n <= 3 * 4] for j in range(k): _.game.moveMove(1, _, _.game.s.rows[i], frames = frames) n = n - k if __debug__: if len(_.cards) == len(_.cards): pass elif not len(_.cards) == 0: raise AssertionError 0 if n == 0 else range(k) return num_cards class Cruel(CastlesInSpain): Talon_Class = StackWrapper(Cruel_Talon, max_rounds = -1) RowStack_Class = StackWrapper(SS_RowStack, base_rank = NO_RANK) def createGame(_): CastlesInSpain.createGame(_, rows = 12) def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): CastlesInSpain.startGame(_, flip = (1, 1, 1)) _.s.talon.dealRow(rows = _.s.foundations) registerGame(GameInfo(83, CastlesInSpain, 'Castles in Spain', GI.GT_BAKERS_DOZEN, 1, 0)) registerGame(GameInfo(84, Martha, 'Martha', GI.GT_BAKERS_DOZEN, 1, 0)) registerGame(GameInfo(31, BakersDozen, "Baker's Dozen", GI.GT_BAKERS_DOZEN, 1, 0)) registerGame(GameInfo(85, SpanishPatience, 'Spanish Patience', GI.GT_BAKERS_DOZEN, 1, 0)) registerGame(GameInfo(86, GoodMeasure, 'Good Measure', GI.GT_BAKERS_DOZEN, 1, 0)) registerGame(GameInfo(104, Cruel, 'Cruel', GI.GT_BAKERS_DOZEN, 1, -1)) class Fan_Hint(CautiousDefaultHint): pass class Fan(Game): Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = KingSS_RowStack Hint_Class = Fan_Hint def createGame(_, rows = (5, 5, 5, 3), playcards = 9): (l, s) = (Layout(_, XOFFSET = 10), _.s) w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET) w = min(3 * l.XS, w) w = w + 1 & ~1 _.setSize(l.XM + max(rows) * w, l.YM + (1 + len(rows)) * l.YS) (x, y) = (l.XM + w, l.YM) for j in range(_.gameinfo.decks): for i in range(4): s.foundations.append(_.Foundation_Class(x, y, _, suit = i)) x = x + w / _.gameinfo.decks for i in range(len(rows)): (x, y) = (l.XM, y + l.YS) for j in range(rows[i]): stack = _.RowStack_Class(x, y, _, max_move = 1, max_accept = 1) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.rows.append(stack) x = x + w (x, y) = (_.width - l.XS, _.height - l.YS) s.talon = _.Talon_Class(x, y, _) l.defaultStackGroups() return l def startGame(_): for i in range(2): _.s.talon.dealRow(rows = _.s.rows[:17], frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () class ScotchPatience(Fan): Foundation_Class = AC_FoundationStack RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK) class Shamrocks_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 (c1, c2) = (_.cards[-1], cards[0]) if not c1.rank == (c2.rank + 1) % _.cap.mod: pass return c2.rank == (c1.rank + 1) % _.cap.mod class Shamrocks(Fan): RowStack_Class = StackWrapper(Shamrocks_RowStack, base_rank = NO_RANK, max_cards = 3) class LaBelleLucie_Talon(TalonStack): def canDealCards(_): if _.round != _.max_rounds: pass return not _.game.isGameWon() def dealCards(_, sound = 0): n = _.redealCards1() if n == 0: return 0 _.redealCards2() if sound: _.game.startDealSample() _.redealCards3() if sound: _.game.stopSamples() return n def redealCards1(_): if not __debug__ and len(_.cards) == 0: raise AssertionError num_cards = 0 for r in _.game.s.rows: pass if not __debug__ and len(_.cards) == num_cards: raise AssertionError None if r.cards else _.game.s.rows return num_cards def redealCards2(_): if not __debug__ and _.round != _.max_rounds: raise AssertionError if not __debug__ and _.cards: raise AssertionError _.game.shuffleStackMove(_) _.game.nextRoundMove(_) def redealCards3(_, face_up = 1): to_stacks = _.game.s.rows n = min(len(_.cards), 3 * len(to_stacks)) for i in range(3): j = (n / 3, (n + 1) / 3, (n + 2) / 3)[i] frames = (0, 0, 4)[i] for r in to_stacks[:j]: _.game.moveMove(1, _, r, frames = frames) class LaBelleLucie(Fan): Talon_Class = StackWrapper(LaBelleLucie_Talon, max_rounds = 3) RowStack_Class = StackWrapper(SS_RowStack, base_rank = NO_RANK) class SuperFlowerGarden(LaBelleLucie): RowStack_Class = StackWrapper(RK_RowStack, base_rank = NO_RANK) class ThreeShufflesAndADraw_RowStack(SS_RowStack): def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): (game, r) = (_.game, _.game.s.reserves[0]) if to_stack is not r: SS_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow) return None f = _._canDrawCard() if __debug__: if not f and game.draw_done == 0 and ncards == 1: raise AssertionError game.updateStackMove(r, 2 | 16) game.moveMove(1, _, r, frames = frames, shadow = shadow) game.updateStackMove(r, 3 | 64) game.updateStackMove(r, 1 | 16) if 1 or not (game.demo): game.playSample('drop', priority = 200) if frames == 0: frames = -1 game.moveMove(1, _, f, frames = frames, shadow = shadow) old_state = game.enterState(game.S_FILL) game.moveMove(1, r, _, frames = frames, shadow = shadow) game.leaveState(old_state) def _canDrawCard(_): if len(_.cards) >= 2: pile = _.cards[-2:-1] for s in _.game.s.foundations + _.game.s.rows: pass return None class ThreeShufflesAndADraw_ReserveStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 if _.game.draw_done or not from_stack._canDrawCard(): return 0 return 1 def updateModel(_, undo, flags): if not __debug__ and undo == _.game.draw_done: raise AssertionError _.game.draw_done = not (_.game.draw_done) def updateText(_): if _.game.preview > 1 or _.texts.misc is None: return None t = ('X', 'Draw')[_.game.draw_done == 0] _.texts.misc.config(text = t) def prepareView(_): ReserveStack.prepareView(_) if not (_.is_visible) or _.game.preview > 1: return None images = _.game.app.images (x, y) = (_.x + images.CARDW / 2, _.y + images.CARDH / 2) _.texts.misc = MfxCanvasText(_.game.canvas, x, y, anchor = 'center') class ThreeShufflesAndADraw(LaBelleLucie): RowStack_Class = StackWrapper(ThreeShufflesAndADraw_RowStack, base_rank = NO_RANK) def createGame(_): l = LaBelleLucie.createGame(_) s = _.s (x, y) = (s.rows[3].x, s.rows[-1].y) s.reserves.append(ThreeShufflesAndADraw_ReserveStack(x, y, _)) l.defaultStackGroups() _.draw_done = 0 def startGame(_): _.draw_done = 0 _.s.reserves[0].updateText() LaBelleLucie.startGame(_) def _restoreGameHook(_, game): _.draw_done = game.loadinfo.draw_done def _loadGameHook(_, p): _.loadinfo.addattr(draw_done = p.load()) def _saveGameHook(_, p): p.dump(_.draw_done) class Trefoil(LaBelleLucie): GAME_VERSION = 2 Foundation_Class = StackWrapper(SS_FoundationStack, min_cards = 1) def createGame(_): return Fan.createGame(_, rows = (5, 5, 5, 1)) def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): for i in range(2): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) class Intelligence_Talon(LaBelleLucie_Talon): dealToStacks = TalonStack.dealToStacksOrFoundations def redealCards1(_): if not __debug__ and len(_.cards) == 0: raise AssertionError r = _.game.s.reserves[0] num_cards = len(r.cards) _.game.moveMove(len(r.cards), r, _, frames = 0) for r in _.game.s.rows: num_cards = num_cards + len(r.cards) while r.cards: _.game.moveMove(1, r, _, frames = 0) _.game.flipMove(_) continue 0 if not __debug__ and len(_.cards) == num_cards: raise AssertionError _.game.s.rows if not __debug__ and forall((lambda c: not (c.face_up)), _.cards): raise AssertionError return num_cards def redealCards3(_, face_up = 1): for r in _.game.s.rows: while len(r.cards) < 3: _.dealToStacks([ r], frames = 4) continue None if not (_.cards) else _.game.s.rows _.game.moveMove(len(_.cards), _, _.game.s.reserves[0], frames = 0) class Intelligence_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if not __debug__ and _.cards: raise AssertionError (c1, c2) = (_.cards[-1], cards[0]) if c1.suit != c2.suit: return 0 if not c1.rank == (c2.rank + 1) % _.cap.mod: pass return c2.rank == (c1.rank + 1) % _.cap.mod def fillStack(_): if not (_.cards): r = _.game.s.reserves[0] if r.cards: r.dealRow(rows = (_, _, _), sound = 1) class Intelligence_ReserveStack(ReserveStack, DealRow_StackMethods): def canFlipCard(_): return 0 dealToStacks = TalonStack.dealToStacksOrFoundations class Intelligence(Fan): Talon_Class = StackWrapper(Intelligence_Talon, max_rounds = 3) RowStack_Class = StackWrapper(Intelligence_RowStack, base_rank = NO_RANK) def createGame(_): l = Fan.createGame(_) s = _.s (x, y) = (s.talon.x - l.XS, s.talon.y) s.reserves.append(Intelligence_ReserveStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 999999)) l.createText(s.reserves[0], 'sw') l.defaultStackGroups() def startGame(_): talon = _.s.talon for i in range(2): talon.dealRow(frames = 0) _.startDealSample() talon.dealRow() _.moveMove(len(talon.cards), talon, _.s.reserves[0], frames = 0) class BlackHole_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: (r1, r2) = (_.cards[-1].rank, cards[0].rank) if not (r1 + 1) % _.cap.mod == r2: pass return (r2 + 1) % _.cap.mod == r1 return 1 class BlackHole_RowStack(ReserveStack): def clickHandler(_, event): return _.doubleclickHandler(event) class BlackHole(Game): RowStack_Class = StackWrapper(BlackHole_RowStack, max_accept = 0, max_cards = 3) Hint_Class = Fan_Hint def createGame(_, playcards = 5): (l, s) = (Layout(_, XOFFSET = 12), _.s) w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET) _.setSize(l.XM + 5 * w, l.YM + 4 * l.YS) y = l.YM for i in range(5): x = l.XM + i * w s.rows.append(_.RowStack_Class(x, y, _)) for i in range(2): y = y + l.YS for j in (0, 1, 3, 4): x = l.XM + j * w s.rows.append(_.RowStack_Class(x, y, _)) y = y + l.YS for i in range(4): x = l.XM + i * w s.rows.append(_.RowStack_Class(x, y, _)) for r in s.rows: r.CARD_XOFFSET = l.XOFFSET r.CARD_YOFFSET = 0 (x, y) = (l.XM + 2 * w, l.YM + 3 * l.YS / 2) s.foundations.append(BlackHole_Foundation(x, y, _, ANY_SUIT, dir = 0, mod = 13, max_move = 0, max_cards = 52)) l.createText(s.foundations[0], 'ss') (x, y) = (l.XM + 4 * w, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.id == 13, c.suit)), 1) def startGame(_): for i in range(2): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) def getAutoStacks(_, event = None): if event is None: return ((), (), _.sg.dropstacks) else: return ((), _.sg.dropstacks, _.sg.dropstacks) registerGame(GameInfo(56, Fan, 'Fan', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0, altnames = 'Midnight Oil')) registerGame(GameInfo(87, ScotchPatience, 'Scotch Patience', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(57, Shamrocks, 'Shamrocks', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(901, LaBelleLucie, 'La Belle Lucie', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2)) registerGame(GameInfo(132, SuperFlowerGarden, 'Super Flower Garden', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2)) registerGame(GameInfo(128, ThreeShufflesAndADraw, 'Three Shuffles and a Draw', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2)) registerGame(GameInfo(88, Trefoil, 'Trefoil', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 2)) registerGame(GameInfo(227, Intelligence, 'Intelligence', GI.GT_FAN_TYPE, 2, 2)) registerGame(GameInfo(98, BlackHole, 'Black Hole', GI.GT_FAN_TYPE | GI.GT_OPEN, 1, 0)) class BeleagueredCastleType_Hint(CautiousDefaultHint): pass class StreetsAndAlleys(Game): Hint_Class = BeleagueredCastleType_Hint def createGame(_, playcards = 13): (l, s) = (Layout(_, XOFFSET = 12), _.s) w = max(3 * l.XS, l.XS + (playcards - 1) * l.XOFFSET) x0 = l.XM x1 = x0 + w + 2 * l.XM x2 = x1 + l.XS + 2 * l.XM x3 = x2 + w + l.XM _.setSize(x3, l.YM + 4 * l.YS) (x, y) = (x1, l.YM) for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, max_move = 0)) y = y + l.YS for x, y in ((x0, l.YM), (x2, l.YM)): for i in range(4): stack = RK_RowStack(x, y, _, max_move = 1, max_accept = 1) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.rows.append(stack) y = y + l.YS (x, y) = (_.width - l.XS, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) l.defaultStackGroups() def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() for i in range(3): _.s.talon.dealRowAvail() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class BeleagueredCastle(StreetsAndAlleys): def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() for i in range(2): _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class Citadel(StreetsAndAlleys): def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): frames = 4 talon = _.s.talon _.startDealSample() talon.dealRow(rows = _.s.foundations, frames = frames) while talon.cards: for r in _.s.rows: _.flipMove(talon) for s in _.s.foundations: pass continue None if not (talon.cards) else _.s.rows class Fortress_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: if _.cards[-1].suit != cards[0].suit: return 0 (r1, r2) = (_.cards[-1].rank, cards[0].rank) if not (r1 + 1) % _.cap.mod == r2: pass return (r2 + 1) % _.cap.mod == r1 return 1 class Fortress(Game): Layout_Method = Layout.klondikeLayout Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = StackWrapper(Fortress_RowStack, max_accept = 1) Hint_Class = BeleagueredCastleType_Hint def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 10, waste = 0, texts = 0, playcards = 16) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) if l.s.waste: s.waste = WasteStack(l.s.waste.x, l.s.waste.y, _) for r in l.s.foundations: s.foundations.append(_.Foundation_Class(r.x, r.y, _, suit = r.suit)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) l.defaultAll() return l def startGame(_): for i in range(3): _.s.talon.dealRow(frames = 0) _.startDealSample() for i in range(3): _.s.talon.dealRowAvail() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 class Chessboard_Foundation(SS_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, mod = 13, min_cards = 1, max_move = 0) apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap) def acceptsCards(_, from_stack, cards): if not (_.cards): if len(cards) != 1 or not (cards[0].face_up): return 0 if cards[0].suit != _.cap.base_suit: return 0 for s in _.game.s.foundations: pass return 1 return SS_FoundationStack.acceptsCards(_, from_stack, cards) class Chessboard_RowStack(Fortress_RowStack): def canDropCards(_, stacks): if _.game.demo: return Fortress_RowStack.canDropCards(_, stacks) for s in _.game.s.foundations: pass return (None, 0) class Chessboard(Fortress): Foundation_Class = Chessboard_Foundation RowStack_Class = StackWrapper(Chessboard_RowStack, max_accept = 1, mod = 13) def createGame(_): l = Fortress.createGame(_) (tx, ty, ta, tf) = l.getTextAttr(_.s.foundations[-1], 'e') font = getFont('canvas_card', cardw = l.CW) _.texts.info = MfxCanvasText(_.canvas, tx + l.XM, ty, anchor = ta, font = font) def updateText(_): if _.preview > 1: return None t = '' for s in _.s.foundations: pass _.texts.info.config(text = t) registerGame(GameInfo(146, StreetsAndAlleys, 'Streets and Alleys', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(34, BeleagueredCastle, 'Beleaguered Castle', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(145, Citadel, 'Citadel', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(147, Fortress, 'Fortress', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(148, Chessboard, 'Chessboard', GI.GT_BELEAGUERED_CASTLE | GI.GT_OPEN, 1, 0)) class UnionSquare_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if len(_.cards) > 12: return cards[0].rank == 25 - len(_.cards) else: return cards[0].rank == len(_.cards) class UnionSquare_RowStack(OpenStack): def __init__(_, x, y, game, **cap): kwdefault(cap, mod = 8192, dir = 0, base_rank = ANY_RANK, max_accept = 1, max_move = 1) apply(OpenStack.__init__, (_, x, y, game), cap) _.CARD_YOFFSET = 1 def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 if not (_.cards): return 1 if cards[0].suit != _.cards[0].suit: return 0 if len(_.cards) == 1: card_dir = cards[0].rank - _.cards[-1].rank if not card_dir == 1: pass return card_dir == -1 else: stack_dir = (_.cards[1].rank - _.cards[0].rank) % _.cap.mod return (_.cards[-1].rank + stack_dir) % _.cap.mod == cards[0].rank def getBottomImage(_): return _.game.app.images.getReserveBottom() class UnionSquare(Game): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_, YM = 18), _.s) _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 's') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 's') for i in range(4): x = 3 * l.XS for j in range(4): s.rows.append(UnionSquare_RowStack(x, y, _)) x = x + l.XS y = y + l.YS (x, y) = (8 * l.XS, l.YM) for i in range(4): stack = UnionSquare_Foundation(x, y, _, i, max_move = 0, dir = 0, max_cards = 26) l.createText(stack, 'sw') s.foundations.append(stack) y = y + l.YS l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () registerGame(GameInfo(35, UnionSquare, 'Union Square', GI.GT_2DECK_TYPE, 2, 0)) class Windmill_Foundation(RK_FoundationStack): def getBottomImage(_): if _.cap.base_rank == ACE: return _.game.app.images.getLetter(ACE) return RK_FoundationStack.getBottomImage(_) class Windmill_RowStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 return from_stack is _.game.s.waste class Windmill(Game): def createGame(_): (l, s) = (Layout(_, XM = 20), _.s) _.setSize(7 * l.XS + l.XM, 5 * l.YS + l.YM + l.YM) x = l.XM y = l.YM s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') (x0, y0) = (x + l.XS, y) for d in ((2, 0), (2, 1), (0, 2), (1, 2), (3, 2), (4, 2), (2, 3), (2, 4)): (x, y) = (x0 + d[0] * l.XS, y0 + d[1] * l.YS) s.rows.append(Windmill_RowStack(x, y, _)) (x, y) = (x0 + 2 * l.XS, y0 + 2 * l.YS) s.foundations.append(Windmill_Foundation(x, y, _, mod = 13, min_cards = 1, max_cards = 52)) for d in ((1, 0.6), (3, 0.6), (1, 3.4), (3, 3.4)): (x, y) = (x0 + d[0] * l.XS, y0 + d[1] * l.YS) s.foundations.append(Windmill_Foundation(x, y, _, base_rank = KING, dir = -1)) l.defaultStackGroups() def _shuffleHook(_, cards): for c in cards: pass cards.remove(c) return cards + [ c] def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = (_.s.foundations[0],)) _.s.talon.dealRow() _.s.talon.dealCards() def fillStack(_, stack): if len(stack.cards) == 0: if stack is _.s.waste and _.s.talon.cards: _.s.talon.dealCards() elif stack in _.s.rows and _.s.waste.cards: _.s.waste.moveMove(1, stack) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () def getAutoStacks(_, event = None): return ((), (), ()) registerGame(GameInfo(30, Windmill, 'Windmill', GI.GT_2DECK_TYPE, 2, 0)) class Pyramid_Hint(DefaultHint): def step010(_, dropstacks, rows): rows = rows + (_.game.s.talon,) return DefaultHint.step010(_, dropstacks, rows) class Pyramid_StackMethods: def acceptsCards(_, from_stack, cards): if _.basicIsBlocked(): return 0 if from_stack is _ and not (_.cards) or len(cards) != 1: return 0 c = _.cards[-1] if c.face_up and cards[0].face_up: pass return cards[0].rank + c.rank == 11 def _dropKingClickHandler(_, event): if not (_.cards): return 0 c = _.cards[-1] if c.face_up and c.rank == KING and not _.basicIsBlocked(): _.game.playSample('autodrop', priority = 20) _.playMoveMove(1, _.game.s.foundations[0], sound = 0) return 1 return 0 def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1): _.game.playSample('droppair', priority = 200) if __debug__: if not n == 1 and _.acceptsCards(other_stack, [ other_stack.cards[-1]]): raise AssertionError old_state = _.game.enterState(_.game.S_FILL) f = _.game.s.foundations[0] _.game.moveMove(n, _, f, frames = frames, shadow = shadow) _.game.moveMove(n, other_stack, f, frames = frames, shadow = shadow) _.game.leaveState(old_state) _.fillStack() other_stack.fillStack() def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if to_stack in _.game.s.foundations: _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow) _.fillStack() else: _._dropPairMove(ncards, to_stack, frames = -1, shadow = shadow) class Pyramid_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 return cards[0].rank == KING class Pyramid_Talon(Pyramid_StackMethods, FaceUpWasteTalonStack): def clickHandler(_, event): if _._dropKingClickHandler(event): return 1 return FaceUpWasteTalonStack.clickHandler(_, event) def canDealCards(_): if not FaceUpWasteTalonStack.canDealCards(_): return 0 return not _.game.isGameWon() def canDropCards(_, stacks): if _.cards: cards = _.cards[-1:] for s in stacks: pass return (None, 0) class Pyramid_Waste(Pyramid_StackMethods, WasteStack): def clickHandler(_, event): if _._dropKingClickHandler(event): return 1 return WasteStack.clickHandler(_, event) class Pyramid_RowStack(Pyramid_StackMethods, OpenStack): def __init__(_, x, y, game): OpenStack.__init__(_, x, y, game, max_accept = 1, max_cards = 2) _.CARD_YOFFSET = 1 STEP = (1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6) def basicIsBlocked(_): (r, step) = (_.game.s.rows, _.STEP) (i, n) = (_.id, 1) while i < 21: i = i + step[i] n = n + 1 for j in range(i, i + n): pass continue None if r[j].cards else range(i, i + n) return 0 def clickHandler(_, event): if _._dropKingClickHandler(event): return 1 return OpenStack.clickHandler(_, event) class Pyramid(Game): Hint_Class = Pyramid_Hint def createGame(_, rows = 4): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS) for i in range(7): x = l.XM + (8 - i) * l.XS / 2 y = l.YM + i * l.YS / 2 for j in range(i + 1): s.rows.append(Pyramid_RowStack(x, y, _)) x = x + l.XS (x, y) = (l.XM, l.YM) s.talon = Pyramid_Talon(x, y, _, max_rounds = 3, max_accept = 1) l.createText(s.talon, 'se') (tx, ty, ta, tf) = l.getTextAttr(s.talon, 'ne') s.talon.texts.rounds = MfxCanvasText(_.canvas, tx, ty, anchor = ta) y = y + l.YS s.waste = Pyramid_Waste(x, y, _, max_accept = 1) l.createText(s.waste, 'se') (x, y) = (_.width - l.XS, l.YM) s.foundations.append(Pyramid_Foundation(x, y, _, suit = ANY_SUIT, dir = 0, base_rank = ANY_RANK, max_move = 0, max_cards = 52)) _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.openstacks = s.rows + _.sg.talonstacks _.sg.dropstacks = s.rows + _.sg.talonstacks def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def getAutoStacks(_, event = None): return (_.sg.dropstacks, _.sg.dropstacks, ()) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank + card2.rank == 11 class RelaxedPyramid(Pyramid): def isGameWon(_): return getNumberOfFreeStacks(_.s.rows) == len(_.s.rows) class Thirteen(Pyramid): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(7 * l.XS + l.XM, 5 * l.YS + l.YM) for i in range(7): x = l.XM + (6 - i) * l.XS / 2 y = l.YM + l.YS + i * l.YS / 2 for j in range(i + 1): s.rows.append(Pyramid_RowStack(x, y, _)) x = x + l.XS (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 's') x = x + l.XS s.waste = Pyramid_Waste(x, y, _) l.createText(s.waste, 's') s.waste.CARD_XOFFSET = 14 (x, y) = (_.width - l.XS, l.YM) s.foundations.append(Pyramid_Foundation(x, y, _, suit = ANY_SUIT, dir = 0, base_rank = ANY_RANK, max_move = 0, max_cards = 999999)) _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.openstacks = s.rows + _.sg.talonstacks _.sg.dropstacks = s.rows + _.sg.talonstacks def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[:21], flip = 0) _.s.talon.dealRow(rows = _.s.rows[21:]) _.s.talon.dealCards() registerGame(GameInfo(38, Pyramid, 'Pyramid', GI.GT_PAIRING_TYPE, 1, 2)) registerGame(GameInfo(193, RelaxedPyramid, 'Relaxed Pyramid', GI.GT_PAIRING_TYPE | GI.GT_RELAXED, 1, 2)) class PileOn_RowStack(RK_RowStack): def getBottomImage(_): return _.game.app.images.getReserveBottom() class PileOn(Game): Hint_Class = DefaultHint def createGame(_, playcards = 4): (l, s) = (Layout(_, XOFFSET = 16), _.s) w = max(2 * l.XS, l.XS + (playcards - 1) * l.XOFFSET + 2 * l.XM) _.setSize(l.XM + 4 * w, l.YM + 4 * l.YS) y = l.YM for i in range(4): x = l.XM for j in range(4): stack = PileOn_RowStack(x, y, _, dir = 0, max_cards = 4) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.rows.append(stack) x = x + w y = y + l.YS (x, y) = (_.width - l.XS, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) _.sg.openstacks = s.rows _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows def startGame(_): r = _.s.rows[:13] for i in range(3): _.s.talon.dealRow(rows = r, frames = 0) _.startDealSample() _.s.talon.dealRow(rows = r) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def isGameWon(_): for r in _.s.rows: if r.cards: if len(r.cards) != 4 or not r._isSequence(r.cards): return 0 return 1 def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank == card2.rank registerGame(GameInfo(41, PileOn, 'PileOn', GI.GT_1DECK_TYPE, 1, 0)) class Bristol_Hint(CautiousDefaultHint): BONUS_CREATE_EMPTY_ROW = 0 BONUS_CAN_DROP_ALL_CARDS = 0 BONUS_CAN_CREATE_EMPTY_ROW = 0 def _getMovePileScore(_, score, color, r, t, pile, rpile): if not (r in _.game.s.reserves): score = score - 10000 if len(pile) == len(r.cards): return (-1, color) return CautiousDefaultHint._getMovePileScore(_, score, color, r, t, pile, rpile) class Bristol_Talon(TalonStack): def dealCards(_, sound = 0): return _.dealRowAvail(rows = _.game.s.reserves, sound = sound) class Bristol(Game): Layout_Method = Layout.klondikeLayout Hint_Class = Bristol_Hint def createGame(_, **layout): (l, s) = (Layout(_, XOFFSET = 10), _.s) _.setSize(l.XM + 10 * l.XS, l.YM + 5 * l.YS) (x, y) = (l.XM + 3 * l.XS, l.YM) for i in range(4): s.foundations.append(RK_FoundationStack(x, y, _, max_move = 0)) x = x + l.XS for i in range(2): y = l.YM + (i * 2 + 3) * l.YS / 2 for j in range(4): x = l.XM + j * 5 * l.XS / 2 stack = RK_RowStack(x, y, _, base_rank = NO_RANK, max_move = 1) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.rows.append(stack) (x, y) = (l.XM + 3 * l.XS, l.YM + 4 * l.YS) s.talon = Bristol_Talon(x, y, _) l.createText(s.talon, 'sw') for i in range(3): x = x + l.XS s.reserves.append(ReserveStack(x, y, _, max_accept = 0, max_cards = 999999)) _.sg.openstacks = s.foundations + s.rows _.sg.talonstacks = [ s.talon] _.sg.dropstacks = s.rows + s.reserves def _shuffleHook(_, cards): (i, n) = (0, len(_.s.rows)) kings = [] for c in cards[:24]: i = i + 1 for i in kings: j = i % n while j < i: j = j + n continue None if c.rank == KING else cards[:24] if cards[j].rank != KING else kings cards.reverse() return cards def startGame(_): r = _.s.rows for i in range(2): _.s.talon.dealRow(rows = r, frames = 0) _.startDealSample() _.s.talon.dealRow(rows = r) _.s.talon.dealCards() class Belvedere(Bristol): def _shuffleHook(_, cards): for c in cards: pass cards = Bristol._shuffleHook(_, cards) return cards[:-24] + [ c] + cards[-24:] def startGame(_): r = _.s.rows for i in range(2): _.s.talon.dealRow(rows = r, frames = 0) _.startDealSample() _.s.talon.dealRow(rows = r) if not __debug__ and _.s.talon.cards[-1].rank == ACE: raise AssertionError 0 _.s.talon.dealRow(rows = _.s.foundations[:1]) _.s.talon.dealCards() registerGame(GameInfo(42, Bristol, 'Bristol', GI.GT_FAN_TYPE, 1, 0)) registerGame(GameInfo(214, Belvedere, 'Belvedere', GI.GT_FAN_TYPE, 1, 0)) class AcesUp_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 c = cards[0] for s in _.game.s.rows: if s is not from_stack and s.cards and s.cards[-1].suit == c.suit: if s.cards[-1].rank > c.rank or s.cards[-1].rank == ACE: return c.rank != ACE return 0 class AcesUp_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 return len(_.cards) == 0 clickHandler = BasicRowStack.doubleclickHandler class AcesUp(Game): Talon_Class = DealRowTalonStack RowStack_Class = StackWrapper(AcesUp_RowStack, max_accept = 1) def createGame(_, **layout): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) s.talon = _.Talon_Class(x, y, _) l.createText(s.talon, 'ss') x = x + 3 * l.XS / 2 for i in range(4): s.rows.append(_.RowStack_Class(x, y, _)) x = x + l.XS x = l.XM + 6 * l.XS stack = AcesUp_Foundation(x, y, _, ANY_SUIT, max_move = 0, dir = 0, base_rank = ANY_RANK, max_cards = 48) l.createText(stack, 'ss') s.foundations.append(stack) l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() def isGameWon(_): if len(_.s.foundations[0].cards) != 48: return 0 for s in _.s.rows: pass return 1 def getAutoStacks(_, event = None): if event is None: return (_.sg.dropstacks, (), _.sg.dropstacks) else: return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks) class Fortunes(AcesUp): RowStack_Class = StackWrapper(AcesUp_RowStack, max_move = 999999, max_accept = 999999) class RussianAces_Talon(DealRowTalonStack): def dealCards(_, sound = 0): rows = filter((lambda s: not (s.cards)), _.game.s.rows) if not rows: rows = _.game.s.rows return _.dealRowAvail(rows = rows, sound = sound) class RussianAces(AcesUp): Talon_Class = RussianAces_Talon class PerpetualMotion_Talon(DealRowTalonStack): def canDealCards(_): if _.game.demo and _.game.moves.index >= 500: return 0 return not _.game.isGameWon() def dealCards(_, sound = 0): if _.cards: return DealRowTalonStack.dealCards(_, sound = sound) (game, num_cards) = (_.game, len(_.cards)) rows = list(game.s.rows)[:] rows.reverse() for r in rows: while r.cards: num_cards = num_cards + 1 game.moveMove(1, r, _, frames = 4) continue None if _.cards[-1].face_up else rows if not __debug__ and len(_.cards) == num_cards: raise AssertionError return DealRowTalonStack.dealCards(_, sound = sound) class PerpetualMotion_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 return isRankSequence(cards, dir = 0) class PerpetualMotion_RowStack(RK_RowStack): def canDropCards(_, stacks): pile = _.getPile() if not pile or len(pile) != 4: return (None, 0) for s in stacks: pass return (None, 0) class PerpetualMotion(Game): def createGame(_, **layout): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) s.talon = PerpetualMotion_Talon(x, y, _, max_rounds = -1) l.createText(s.talon, 'ss') x = x + 3 * l.XS / 2 for i in range(4): s.rows.append(PerpetualMotion_RowStack(x, y, _, dir = 0, base_rank = NO_RANK)) x = x + l.XS x = l.XM + 6 * l.XS stack = PerpetualMotion_Foundation(x, y, _, ANY_SUIT, base_rank = ANY_RANK, max_cards = 52, max_move = 0, min_accept = 4, max_accept = 4) l.createText(stack, 'ss') s.foundations.append(stack) l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank == card2.rank registerGame(GameInfo(903, AcesUp, 'Aces Up', GI.GT_1DECK_TYPE, 1, 0, altnames = "Idiot's Delight")) registerGame(GameInfo(206, Fortunes, 'Fortunes', GI.GT_1DECK_TYPE, 1, 0)) registerGame(GameInfo(213, RussianAces, 'Russian Aces', GI.GT_1DECK_TYPE, 1, 0)) registerGame(GameInfo(130, PerpetualMotion, 'Perpetual Motion', GI.GT_1DECK_TYPE, 1, -1, altnames = 'First Law')) class Montana_Hint(DefaultHint): def computeHints(_): game = _.game (RLEN, RSTEP, RBASE) = (game.RLEN, game.RSTEP, game.RBASE) freerows = filter((lambda s: not (s.cards)), game.s.rows) for r in game.s.rows: if __debug__: if not len(r.cards) == 1 and r.cards[-1].face_up: raise AssertionError None if not (r.cards) else game.s.rows (c, pile, rpile) = (r.cards[0], r.cards, []) if r.id % RSTEP > 0: left = game.s.rows[r.id - 1] else: left = None if c.rank == RBASE: continue for t in freerows: if _.shallMovePile(r, t, pile, rpile): _.addHint(score, 1, r, t) class Montana_Talon(TalonStack): def canDealCards(_): if _.round != _.max_rounds: pass return not _.game.isGameWon() def dealCards(_, sound = 0): game = _.game (RLEN, RSTEP, RBASE) = (game.RLEN, game.RSTEP, game.RBASE) num_cards = 0 if not __debug__ and len(_.cards) == 0: raise AssertionError rows = game.s.rows stacks = [] gaps = [ None] * 4 for g in range(4): i = g * RSTEP r = rows[i] for j in range(RSTEP): r = rows[i + j] if not in_sequence: stacks.append(r) if gaps[g] is None: gaps[g] = r if r.cards: game.moveMove(1, r, _, frames = 0) num_cards = num_cards + 1 if not __debug__ and len(_.cards) == num_cards: raise AssertionError if not __debug__ and len(stacks) == num_cards + len(gaps): raise AssertionError if num_cards == 0: return 0 if sound: game.startDealSample() game.shuffleStackMove(_) game.nextRoundMove(_) spaces = _.getRedealSpaces(stacks, gaps) for r in stacks: pass if not __debug__ and len(_.cards) == 0: raise AssertionError None if not (r in spaces) else stacks if sound: game.stopSamples() return num_cards def getRedealSpaces(_, stacks, gaps): return gaps class Montana_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if _.id % _.game.RSTEP == 0: return cards[0].rank == _.game.RBASE left = _.game.s.rows[_.id - 1] if left.cards and left.cards[-1].suit == cards[0].suit: pass return left.cards[-1].rank + 1 == cards[0].rank def clickHandler(_, event): if not (_.cards): return _.quickPlayHandler(event) return BasicRowStack.clickHandler(_, event) prepareBottom = Stack.prepareInvisibleBottom def getBottomImage(_): return _.game.app.images.getReserveBottom() class Montana(Game): Talon_Class = Montana_Talon RowStack_Class = Montana_RowStack Hint_Class = Montana_Hint (RLEN, RSTEP, RBASE) = (52, 13, 1) def createGame(_): (l, s) = (Layout(_, XM = 4), _.s) _.setSize(l.XM + _.RSTEP * l.XS, l.YM + 5 * l.YS) for i in range(4): (x, y) = (l.XM, l.YM + i * l.YS) for j in range(_.RSTEP): s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 1)) x = x + l.XS x = l.XM + (_.RSTEP - 1) * l.XS / 2 s.talon = _.Talon_Class(x, _.height - l.YS, _, max_rounds = 3) l.defaultStackGroups() def startGame(_): frames = 0 for i in range(52): c = _.s.talon.cards[-1] if c.rank == ACE: _.s.talon.dealRow(rows = _.s.internals, frames = 0) elif frames == 0 and i >= 39: _.startDealSample() frames = 4 _.s.talon.dealRow(rows = (_.s.rows[i],), frames = frames) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError def isGameWon(_): rows = _.s.rows for i in range(0, _.RLEN, _.RSTEP): suit = rows[i].cards[-1].suit for j in range(_.RSTEP - 1): r = rows[i + j] return 1 def getHighlightPilesStacks(_): return () def getAutoStacks(_, event = None): return (_.sg.dropstacks, (), _.sg.dropstacks) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getQuickPlayScore(_, ncards, from_stack, to_stack): if from_stack.cards: if from_stack.id % _.RSTEP == 0 and from_stack.cards[-1].rank == _.RBASE: return -1 return 1 class Spaces_Talon(Montana_Talon): def getRedealSpaces(_, stacks, gaps): spaces = [] while len(spaces) != 4: r = _.game.random.choice(stacks) if not (r in spaces): spaces.append(r) return spaces class Spaces(Montana): Talon_Class = Spaces_Talon class BlueMoon(Montana): (RLEN, RSTEP, RBASE) = (56, 14, 0) def startGame(_): frames = 0 j = 0 for i in range(52): if i == 39: _.startDealSample() frames = 4 _.s.talon.dealRow(rows = (_.s.rows[j],), frames = frames) j = j + 1 if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError ace_rows = filter((lambda r: if r.cards: passr.cards[-1].rank == ACE), _.s.rows) j = 0 for r in ace_rows: _.moveMove(1, r, _.s.rows[j]) j = j + 14 class RedMoon(BlueMoon): def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): frames = 0 r = _.s.rows _.s.talon.dealRow(rows = (r[0], r[14], r[28], r[42]), frames = frames) for i in range(4): n = i * 14 + 2 _.s.talon.dealRow(rows = r[n:n + 12], frames = frames) registerGame(GameInfo(53, Montana, 'Montana', GI.GT_MONTANA, 1, 2, si = { 'ncards': 48 }, altnames = 'Gaps')) registerGame(GameInfo(116, Spaces, 'Spaces', GI.GT_MONTANA, 1, 2, si = { 'ncards': 48 })) registerGame(GameInfo(63, BlueMoon, 'Blue Moon', GI.GT_MONTANA, 1, 2)) registerGame(GameInfo(117, RedMoon, 'Red Moon', GI.GT_MONTANA, 1, 2)) class PasDeDeux_Hint(AbstractHint): def getDistance(_, stack, card): d = 0 if card.rank != stack.id % 13: d = d + 1 if card.suit != stack.id / 13: d = d + 1 return d def computeHints(_): rows = [] for r in _.game.s.rows: pass for r in rows: r1_d = _.getDistance(r, r.cards[-1]) (column, row) = (r.id % 13, r.id / 13) stack_ids = range(column, 52, 13) + range(13 * row, 13 * row + 13) for i in stack_ids: t = _.game.s.rows[i] if not __debug__ and t.acceptsCards(r, r.cards): raise AssertionError 0 if t is r else stack_ids t1_d = _.getDistance(t, t.cards[-1]) r2_d = _.getDistance(t, r.cards[-1]) t2_d = _.getDistance(r, t.cards[-1]) (rw, tw) = (3, 2) c = _.game.cards[t.cards[-1].id - 52] if 1 and c in _.game.s.waste.cards: rw = rw - 1 score = int((rw * r1_d + tw * t1_d - (rw * r2_d + tw * t2_d)) * 1000) if score > 0: _.addHint(score, 1, r, t) class PasDeDeux_Waste(WasteStack): def canFlipCard(_): return 0 class PasDeDeux_RowStack(ReserveStack): def canMoveCards(_, cards): if not ReserveStack.canMoveCards(_, cards): return 0 if not (_.game.s.waste.cards): return 0 c = _.game.s.waste.cards[-1] if c.face_up and cards[0].suit == c.suit: pass return cards[0].rank == c.rank def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 return _.game.isNeighbour(from_stack, _) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if __debug__: if not ncards == 1 and to_stack in _.game.s.rows: raise AssertionError if not __debug__ and len(to_stack.cards) == 1: raise AssertionError _._swapPairMove(ncards, to_stack, frames = -1, shadow = 0) if _.game.s.talon.canDealCards(): _.game.s.talon.dealCards() elif not __debug__ and _.game.s.waste.cards[-1].face_up: raise AssertionError _.game.flipMove(_.game.s.waste) def _swapPairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game old_state = game.enterState(game.S_FILL) swap = game.s.internals[0] game.moveMove(n, _, swap, frames = 0) game.moveMove(n, other_stack, _, frames = frames, shadow = shadow) game.moveMove(n, swap, other_stack, frames = 0) game.leaveState(old_state) def getBottomImage(_): suit = _.id / 13 return _.game.app.images.getSuitBottom(suit) def quickPlayHandler(_, event, from_stacks = None, to_stacks = None): for r in _.game.s.rows: if r.canMoveCards(r.cards): break return 0 class PasDeDeux(Game): Hint_Class = PasDeDeux_Hint def createGame(_): (l, s) = (Layout(_, XM = 4), _.s) _.setSize(l.XM + 13 * l.XS, l.YM + 5 * l.YS) for i in range(4): for j in range(13): (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS) s.rows.append(PasDeDeux_RowStack(x, y, _, max_accept = 1, max_cards = 2)) (x, y) = (_.width - 2 * l.XS, _.height - l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 2) l.createText(s.talon, 'se') s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.XS, y, anchor = 'nw') x = x - l.XS s.waste = PasDeDeux_Waste(x, y, _, max_move = 0) l.createText(s.waste, 'sw') s.internals.append(InvisibleStack(_)) l.defaultStackGroups() def shuffle(_): _.shuffleSeparateDecks() def startGame(_): _.startDealSample() _.s.talon.dealRow(frames = 4) _.s.talon.dealCards() def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) def isGameWon(_): for r in _.s.rows: c = r.cards[-1] if c.suit != r.id / 13 or c.rank != r.id % 13: return 0 return 1 def isNeighbour(_, stack1, stack2): (column1, row1) = (stack1.id % 13, stack1.id / 13) (column2, row2) = (stack2.id % 13, stack2.id / 13) if not column1 == column2: pass return row1 == row2 def getHighlightPilesStacks(_): return ((_.s.rows, 1),) registerGame(GameInfo(153, PasDeDeux, 'Pas de Deux', GI.GT_MONTANA | GI.GT_SEPARATE_DECKS, 2, 1)) class RoyalCotillion_Foundation(SS_FoundationStack): def getBottomImage(_): if _.cap.base_rank == 1: return _.game.app.images.getLetter(1) return _.game.app.images.getSuitBottom(_.cap.base_suit) class RoyalCotillion(Game): Foundation_Class = RoyalCotillion_Foundation def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 10 * l.XS, l.YM + 4 * l.YS) for i in range(4): (x, y) = (l.XM + i * l.XS, l.YM) s.rows.append(BasicRowStack(x, y, _, max_accept = 0)) for i in range(4): (x, y) = (l.XM + 4 * l.XS, l.YM + i * l.YS) s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13)) x = x + l.XS s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13, base_rank = 1)) for i in range(4): for j in range(4): (x, y) = (l.XM + (j + 6) * l.XS, l.YM + i * l.YS) s.reserves.append(ReserveStack(x, y, _, max_accept = 0)) (x, y) = (l.XM + l.XS, _.height - l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'sw') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se') l.defaultStackGroups() def startGame(_): _.s.talon.dealRow(rows = _.s.reserves, frames = 0) _.startDealSample() for i in range(3): _.s.talon.dealRow() _.s.talon.dealCards() def fillStack(_, stack): if not (stack.cards): old_state = _.enterState(_.S_FILL) if stack is _.s.waste and _.s.talon.cards: _.s.talon.dealCards() elif stack in _.s.reserves and _.s.waste.cards: _.s.waste.moveMove(1, stack) _.leaveState(old_state) def getHighlightPilesStacks(_): return () def getAutoStacks(_, event = None): if event is None: return (_.sg.dropstacks, (), _.sg.dropstacks) else: return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks) class OddAndEven(RoyalCotillion): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) for i in range(4): s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13)) x = x + l.XS for i in range(4): s.foundations.append(_.Foundation_Class(x, y, _, i, dir = 2, mod = 13, base_rank = 1)) x = x + l.XS for i in range(2): (x, y) = (l.XM + (4, 3)[i] * l.XS, l.YM + (i + 1) * l.YS) for j in range((4, 5)[i]): s.reserves.append(ReserveStack(x, y, _, max_accept = 0)) x = x + l.XS (x, y) = (l.XM, _.height - l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 2) l.createText(s.talon, 'nn') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'nn') l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealCards() class Kingdom(RoyalCotillion): Foundation_Class = RK_FoundationStack def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) for i in range(8): s.foundations.append(_.Foundation_Class(x, y, _, ANY_SUIT)) x = x + l.XS (x, y) = (l.XM, y + l.YS) for i in range(8): s.reserves.append(ReserveStack(x, y, _, max_accept = 0)) x = x + l.XS (x, y) = (l.XM + 3 * l.XS, y + 3 * l.YS / 2) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'sw') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se') l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit)), 1) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = (_.s.foundations[0],)) _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealCards() class Alhambra_Waste(WasteStack): def acceptsCards(_, from_stack, cards): if not WasteStack.acceptsCards(_, from_stack, cards): return 0 if not (_.cards): return 0 (c1, c2) = (_.cards[-1], cards[0]) if not c1.suit == c2.suit and (c1.rank + 1) % _.cap.mod == c2.rank: pass return (c2.rank + 1) % _.cap.mod == c1.rank class Alhambra(Game): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, suit = i, max_move = 0)) x = x + l.XS for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, suit = i, max_move = 0, base_rank = KING, dir = -1)) x = x + l.XS (x, y) = (l.XM, y + l.YS) for i in range(8): s.reserves.append(BasicRowStack(x, y, _, max_accept = 0)) x = x + l.XS (x, y) = (l.XM + 3 * l.XS, y + 2 * l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = 3) l.createText(s.talon, 'sw') x = x + l.XS s.waste = Alhambra_Waste(x, y, _, mod = 13, max_accept = 1) l.createText(s.waste, 'se') s.rows.append(s.waste) l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: if c.deck == 0: pass(c.rank in (0, 12), (c.rank, c.suit))), 8) def startGame(_): _.s.talon.dealRow(rows = _.s.foundations, frames = 0) for i in range(3): _.s.talon.dealRow(rows = _.s.reserves, frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.reserves) class Carpet(Game): Foundation_Class = SS_FoundationStack def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 9 * l.XS, l.YM + 4 * l.YS) for i in range(4): for j in range(5): (x, y) = (l.XM + (j + 3) * l.XS, l.YM + i * l.YS) s.rows.append(ReserveStack(x, y, _)) for i in range(4): (dx, dy) = ((2, 1), (8, 1), (2, 2), (8, 2))[i] (x, y) = (l.XM + dx * l.XS, l.YM + dy * l.YS) s.foundations.append(_.Foundation_Class(x, y, _, i)) (x, y) = (l.XM, l.YM) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'se') y = y + l.YS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se') l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) _.s.talon.dealRow() _.s.talon.dealCards() registerGame(GameInfo(54, RoyalCotillion, 'Royal Cotillion', GI.GT_2DECK_TYPE, 2, 0)) registerGame(GameInfo(55, OddAndEven, 'Odd and Even', GI.GT_2DECK_TYPE, 2, 1)) registerGame(GameInfo(143, Kingdom, 'Kingdom', GI.GT_2DECK_TYPE, 2, 0)) registerGame(GameInfo(234, Alhambra, 'Alhambra', GI.GT_2DECK_TYPE, 2, 2)) registerGame(GameInfo(97, Carpet, 'Carpet', GI.GT_1DECK_TYPE, 1, 0)) class Osmosis_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if not __debug__ and len(cards) == 1: raise AssertionError (max_s, max_cards) = (None, -1) for s in _.game.s.foundations: pass if len(_.cards) < max_cards: if cards[0].rank != max_s.cards[len(_.cards)].rank: return 0 return 1 class Osmosis(Game): def createGame(_, max_rounds = -1, num_deal = 1): (l, s) = (Layout(_, XOFFSET = 12), _.s) _.setSize(l.XM + 7 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) for i in range(4): stack = RK_RowStack(x, y, _, max_move = 1, max_accept = 0) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.rows.append(stack) y = y + l.YS (x, y) = (l.XM + 2 * l.XS, l.YM) for i in range(4): stack = Osmosis_Foundation(x, y, _, i, base_rank = ANY_RANK, max_move = 0) (stack.CARD_XOFFSET, stack.CARD_YOFFSET) = (l.XOFFSET, 0) s.foundations.append(stack) y = y + l.YS (x, y) = (_.width - l.XS, l.YM + l.YS) s.talon = WasteTalonStack(x, y, _, max_rounds = max_rounds, num_deal = num_deal) l.createText(s.talon, 'sw') y = y + l.YS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'sw') l.defaultStackGroups() def startGame(_, flip = 0): base_card = _.s.talon.getCard() n = base_card.suit * _.gameinfo.decks to_stack = _.s.foundations[n] _.startDealSample() _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, to_stack) for i in range(3): _.s.talon.dealRow(flip = flip) _.s.talon.dealRow() _.s.talon.dealCards() class Peek(Osmosis): def startGame(_): Osmosis.startGame(_, flip = 1) registerGame(GameInfo(59, Osmosis, 'Osmosis', GI.GT_1DECK_TYPE, 1, -1)) registerGame(GameInfo(60, Peek, 'Peek', GI.GT_1DECK_TYPE, 1, -1)) class MonteCarlo_Hint(DefaultHint): pass class MonteCarlo_Talon(TalonStack): def canDealCards(_): free = 0 for r in _.game.s.rows: if not (r.cards): free = 1 elif free: return 1 if free: pass return len(_.cards) def dealCards(_, sound = 0): _.game.updateStackMove(_.game.s.talon, 2 | 16) n = _.game.fillEmptyStacks() _.game.updateStackMove(_.game.s.talon, 1 | 16) return n class MonteCarlo_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 if _.cards[-1].rank != cards[0].rank: return 0 return _.game.isNeighbour(from_stack, _) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if __debug__: if not ncards == 1 and to_stack in _.game.s.rows: raise AssertionError if to_stack.cards: _._dropPairMove(ncards, to_stack, frames = -1, shadow = shadow) else: BasicRowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow) def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game old_state = game.enterState(game.S_FILL) f = game.s.foundations[0] game.updateStackMove(game.s.talon, 2 | 16) if not (game.demo): game.playSample('droppair', priority = 200) game.moveMove(n, _, f, frames = frames, shadow = shadow) game.moveMove(n, other_stack, f, frames = frames, shadow = shadow) _.fillStack() other_stack.fillStack() if _.game.FILL_STACKS_AFTER_DROP: game.fillEmptyStacks() game.updateStackMove(game.s.talon, 1 | 16) game.leaveState(old_state) class MonteCarlo(Game): Talon_Class = MonteCarlo_Talon Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0) RowStack_Class = MonteCarlo_RowStack Hint_Class = MonteCarlo_Hint FILL_STACKS_AFTER_DROP = 0 def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 6.5 * l.XS, l.YM + 5 * l.YS) for i in range(5): for j in range(5): (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS) s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 2, dir = 0, base_rank = NO_RANK)) (x, y) = (l.XM + 11 * l.XS / 2, l.YM) s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK)) l.createText(s.foundations[0], 'ss') y = y + 2 * l.YS s.talon = _.Talon_Class(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss', text_format = '%D') l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank == card2.rank def isNeighbour(_, stack1, stack2): if stack1.id <= stack1.id: pass elif stack1.id <= 24: pass if not None if stack2.id <= stack2.id else stack2.id <= 24: return 0 column = stack2.id % 5 diff = stack1.id - stack2.id if column == 0: return diff in (-5, -4, 1, 5, 6) elif column == 4: return diff in (-6, -5, -1, 4, 5) else: return diff in (-6, -5, -4, -1, 1, 4, 5, 6) def fillEmptyStacks(_): (free, n) = (0, 0) _.startDealSample() for r in _.s.rows: if not __debug__ and len(r.cards) <= 1: raise AssertionError 0 if not (r.cards): free = free + 1 elif free > 0: to_stack = _.allstacks[r.id - free] _.moveMove(1, r, to_stack, frames = 4, shadow = 0) if free > 0: for r in _.s.rows: if not (r.cards): _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, r) n = n + 1 _.stopSamples() return n class Monaco(MonteCarlo): pass class Weddings_Talon(MonteCarlo_Talon): def canDealCards(_): free = 0 for r in _.game.s.rows: if not (r.cards): free = 1 else: k = r.id while k >= 5 and not (_.game.allstacks[k - 5].cards): k = k - 5 continue _.game.s.rows if k != r.id: return 1 if free: pass return len(_.cards) class Weddings(MonteCarlo): Talon_Class = Weddings_Talon def fillEmptyStacks(_): (free, n) = (0, 0) _.startDealSample() for r in _.s.rows: if not __debug__ and len(r.cards) <= 1: raise AssertionError 0 if not (r.cards): free = free + 1 else: k = r.id while k >= 5 and not (_.allstacks[k - 5].cards): k = k - 5 if k != r.id: to_stack = _.allstacks[k] _.moveMove(1, r, to_stack, frames = 4, shadow = 0) if free > 0: for r in _.s.rows: if not (r.cards): _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, r) n = n + 1 _.stopSamples() return n class SimpleCarlo(MonteCarlo): FILL_STACKS_AFTER_DROP = 1 def getAutoStacks(_, event = None): return ((), (), ()) def isNeighbour(_, stack1, stack2): if stack1.id <= stack1.id: pass elif stack1.id <= 24: pass return None if stack2.id <= stack2.id else stack2.id <= 24 class SimplePairs(MonteCarlo): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 6 * l.XS, l.YM + 4 * l.YS) for i in range(3): for j in range(3): (x, y) = (l.XM + (2 * j + 3) * l.XS / 2, l.YM + (2 * i + 1) * l.YS / 2) s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_cards = 2, dir = 0, base_rank = NO_RANK)) (x, y) = (l.XM, l.YM + 3 * l.YS / 2) s.talon = TalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') x = x + 5 * l.XS s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK)) l.createText(s.foundations[0], 'ss') l.defaultStackGroups() def fillStack(_, stack): if stack in _.s.rows: if len(stack.cards) == 0 and len(_.s.talon.cards) > 0: _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, stack) def isNeighbour(_, stack1, stack2): if stack1.id <= stack1.id: pass elif stack1.id <= 15: pass return None if stack2.id <= stack2.id else stack2.id <= 15 class Neighbour_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 return cards[0].rank == KING class Neighbour_RowStack(MonteCarlo_RowStack): def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 if _.cards[-1].rank + cards[0].rank != 11: return 0 return _.game.isNeighbour(from_stack, _) def clickHandler(_, event): if _._dropKingClickHandler(event): return 1 return MonteCarlo_RowStack.clickHandler(_, event) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if not __debug__ and ncards == 1: raise AssertionError if _.cards[-1].rank == KING: if not __debug__ and to_stack in _.game.s.foundations: raise AssertionError BasicRowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow) else: MonteCarlo_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow) def _dropKingClickHandler(_, event): if not (_.cards): return 0 c = _.cards[-1] if c.face_up and c.rank == KING and not _.basicIsBlocked(): _.game.playSample('autodrop', priority = 20) _.playMoveMove(1, _.game.s.foundations[0], sound = 0) return 1 return 0 def fillStack(_): if not (_.cards) and _.game.s.talon.canDealCards(): old_state = _.game.enterState(_.game.S_FILL) _.game.s.talon.dealCards() _.game.leaveState(old_state) class Neighbour(MonteCarlo): Foundation_Class = Neighbour_Foundation RowStack_Class = Neighbour_RowStack FILL_STACKS_AFTER_DROP = 1 def getAutoStacks(_, event = None): return ((), _.sg.dropstacks, ()) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank + card2.rank == 11 class Fourteen_RowStack(MonteCarlo_RowStack): def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 return _.cards[-1].rank + cards[0].rank == 12 class Fourteen(Game): Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0) RowStack_Class = Fourteen_RowStack FILL_STACKS_AFTER_DROP = 0 def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 7 * l.XS, l.YM + 5 * l.YS) for i in (0, 2.5): for j in range(6): (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS) s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK)) (x, y) = (l.XM + 6 * l.XS, l.YM) s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK)) l.createText(s.foundations[0], 'ss') (x, y) = (_.width - l.XS, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) l.defaultStackGroups() def startGame(_): for i in range(3): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.rows[:4]) def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank + card2.rank == 12 class Nestor_RowStack(MonteCarlo_RowStack): def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 return _.cards[-1].rank == cards[0].rank class Nestor(Game): Foundation_Class = StackWrapper(AbstractFoundationStack, max_accept = 0) RowStack_Class = Nestor_RowStack FILL_STACKS_AFTER_DROP = 0 def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS) for j in range(8): (x, y) = (l.XM + j * l.XS, l.YM) s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK)) for j in range(4): (x, y) = (l.XM + (j + 2) * l.XS, l.YM + 3 * l.YS) s.rows.append(_.RowStack_Class(x, y, _, max_move = 1, max_accept = 1, dir = 0, base_rank = NO_RANK)) (x, y) = (_.width - l.XS, _.height - l.YS) s.foundations.append(_.Foundation_Class(x, y, _, suit = ANY_SUIT, max_move = 0, max_cards = 52, base_rank = ANY_RANK)) l.createText(s.foundations[0], 'nn') (x, y) = (l.XM, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) l.defaultStackGroups() def _shuffleHook(_, cards): return cards def startGame(_): for i in range(5): _.s.talon.dealRow(rows = _.s.rows[:8], frames = 0) _.startDealSample() _.s.talon.dealRow() def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) def shallHighlightMatch(_, stack1, card1, stack2, card2): return card1.rank == card2.rank class DerLetzteMonarch_Foundation(SS_FoundationStack): def acceptsCards(_, from_stack, cards): if cards is None: return SS_FoundationStack.acceptsCards(_, from_stack, from_stack.cards) if not SS_FoundationStack.acceptsCards(_, from_stack, cards): return 0 return from_stack in _.game.s.reserves class DerLetzteMonarch_RowStack(ReserveStack): def canDropCards(_, stacks): return (None, 0) def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 if not _.game.isNeighbour(from_stack, _): return 0 return _._getDropStack() is not None def _getDropStack(_): if len(_.cards) != 1: return None for s in _.game.s.foundations: pass for s in _.game.s.reserves: pass return None def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if __debug__: if not ncards == 1 and to_stack in _.game.s.rows: raise AssertionError if not __debug__ and len(to_stack.cards) == 1: raise AssertionError _._handlePairMove(ncards, to_stack, frames = -1, shadow = 0) def _handlePairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game old_state = game.enterState(game.S_FILL) s = other_stack._getDropStack() if not __debug__ and s is not None: raise AssertionError game.moveMove(n, other_stack, s, frames = frames, shadow = shadow) game.moveMove(n, _, other_stack, frames = 0) game.leaveState(old_state) class DerLetzteMonarch_ReserveStack(ReserveStack): def clickHandler(_, event): return _.doubleclickHandler(event) class DerLetzteMonarch(Game): def createGame(_): (l, s) = (Layout(_, XM = 4), _.s) _.setSize(l.XM + 13 * l.XS, l.YM + 5 * l.YS) for i in range(4): for j in range(13): (x, y) = (l.XM + j * l.XS, l.YM + (i + 1) * l.YS) s.rows.append(DerLetzteMonarch_RowStack(x, y, _, max_accept = 1, max_cards = 2)) for i in range(4): (x, y) = (l.XM + (i + 2) * l.XS, l.YM) s.reserves.append(DerLetzteMonarch_ReserveStack(x, y, _, max_accept = 0)) for i in range(4): (x, y) = (l.XM + (i + 7) * l.XS, l.YM) s.foundations.append(DerLetzteMonarch_Foundation(x, y, _, i, max_move = 0)) s.talon = InitialDealTalonStack(l.XM, l.YM, _) _.sg.talonstacks = [ s.talon] _.sg.openstacks = s.foundations + s.rows _.sg.dropstacks = s.rows + s.reserves _.sg.reservestacks = s.reserves def startGame(_): _.s.talon.dealRow(rows = _.s.rows[:39], frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[39:]) def isGameWon(_): c = 0 for s in _.s.foundations: c = c + len(s.cards) return c == 51 def getAutoStacks(_, event = None): return ((), _.s.reserves, ()) def getDemoInfoText(_): return 'Der letzte\nMonarch' def isNeighbour(_, stack1, stack2): if stack1.id <= stack1.id: pass elif stack1.id <= 51: pass if not None if stack2.id <= stack2.id else stack2.id <= 51: return 0 column = stack2.id % 13 diff = stack1.id - stack2.id if column == 0: return diff in (-13, 1, 13) elif column == 12: return diff in (-13, -1, 13) else: return diff in (-13, -1, 1, 13) registerGame(GameInfo(89, MonteCarlo, 'Monte Carlo', GI.GT_PAIRING_TYPE, 1, 0)) registerGame(GameInfo(216, Monaco, 'Monaco', GI.GT_PAIRING_TYPE, 2, 0)) registerGame(GameInfo(212, Weddings, 'Weddings', GI.GT_PAIRING_TYPE, 1, 0)) registerGame(GameInfo(90, SimpleCarlo, 'Simple Carlo', GI.GT_PAIRING_TYPE, 1, 0)) registerGame(GameInfo(91, SimplePairs, 'Simple Pairs', GI.GT_PAIRING_TYPE, 1, 0)) registerGame(GameInfo(92, Neighbour, 'Neighbour', GI.GT_PAIRING_TYPE, 1, 0)) registerGame(GameInfo(96, Fourteen, 'Fourteen', GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(235, Nestor, 'Nestor', GI.GT_PAIRING_TYPE | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(152, DerLetzteMonarch, 'Der letzte Monarch', GI.GT_1DECK_TYPE | GI.GT_OPEN, 1, 0)) class RoyalEast(Game): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 5.5 * l.XS, l.YM + 4 * l.YS) _.base_card = None for i in range(4): (dx, dy) = ((0, 0), (2, 0), (0, 2), (2, 2))[i] (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2) stack = SS_FoundationStack(x, y, _, i, mod = 13, max_move = 0) stack.CARD_YOFFSET = 0 s.foundations.append(stack) for i in range(5): (dx, dy) = ((1, 0), (0, 1), (1, 1), (2, 1), (1, 2))[i] (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2) stack = RK_RowStack(x, y, _, mod = 13, max_move = 1) stack.CARD_YOFFSET = 0 s.rows.append(stack) (x, y) = (l.XM, l.YM + 3 * l.YS / 2) s.talon = WasteTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() def startGame(_): _.base_card = _.s.talon.cards[-1] for s in _.s.foundations: s.cap.base_rank = _.base_card.rank c = _.s.talon.getCard() to_stack = _.s.foundations[c.suit * _.gameinfo.decks] _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, to_stack, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def _restoreGameHook(_, game): _.base_card = _.cards[game.loadinfo.base_card_id] for s in _.s.foundations: s.cap.base_rank = _.base_card.rank def _loadGameHook(_, p): _.loadinfo.addattr(base_card_id = None) _.loadinfo.base_card_id = p.load() def _saveGameHook(_, p): p.dump(_.base_card.id) registerGame(GameInfo(93, RoyalEast, 'Royal East', GI.GT_1DECK_TYPE, 1, 0)) class TamOShanter(Game): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 6 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) s.talon = DealRowTalonStack(x, y, _, max_rounds = 1) l.createText(s.talon, 'ss') for i in range(4): (x, y) = (l.XM + (i + 2) * l.XS, l.YM) s.foundations.append(RK_FoundationStack(x, y, _)) for i in range(4): (x, y) = (l.XM + (i + 2) * l.XS, l.YM + l.YS) s.rows.append(BasicRowStack(x, y, _, max_move = 1, max_accept = 0)) x = x + l.XS l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) class AuldLangSyne(TamOShanter): def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.s.talon.dealRow(rows = _.s.foundations, frames = 0) _.startDealSample() _.s.talon.dealRow() class Strategy_Foundation(SS_FoundationStack): def acceptsCards(_, from_stack, cards): if not SS_FoundationStack.acceptsCards(_, from_stack, cards): return 0 return len(_.game.s.talon.cards) == 0 class Strategy_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if from_stack is _.game.s.talon: pass return len(cards) == 1 def canMoveCards(_, cards): if _.game.s.talon.cards: return 0 return BasicRowStack.canMoveCards(_, cards) def clickHandler(_, event): if _.game.s.talon.cards: _.game.s.talon.playMoveMove(1, _) return 1 return BasicRowStack.clickHandler(_, event) def doubleclickHandler(_, event): if _.game.s.talon.cards: _.game.s.talon.playMoveMove(1, _) return 1 return BasicRowStack.doubleclickHandler(_, event) def getBottomImage(_): return _.game.app.images.getReserveBottom() class Strategy(Game): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 8 * l.XS, l.YM + 4 * l.YS) (x, y) = (l.XM, l.YM) s.talon = OpenTalonStack(x, y, _) l.createText(s.talon, 'se') for i in range(4): (x, y) = (l.XM + (i + 2) * l.XS, l.YM) s.foundations.append(Strategy_Foundation(x, y, _, suit = i, max_move = 0)) for i in range(8): (x, y) = (l.XM + i * l.XS, l.YM + l.YS) s.rows.append(Strategy_RowStack(x, y, _, max_move = 1, max_accept = 1)) x = x + l.XS l.defaultStackGroups() def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) _.s.talon.fillStack() class Interregnum_Foundation(RK_FoundationStack): def acceptsCards(_, from_stack, cards): if not RK_FoundationStack.acceptsCards(_, from_stack, cards): return 0 if len(_.cards) == 12: return from_stack.id == _.id - 8 else: return from_stack in _.game.s.rows class Interregnum(Game): GAME_VERSION = 2 def createGame(_, rows = 8): (l, s) = (Layout(_), _.s) _.setSize(l.XM + max(9, rows) * l.XS, l.YM + 5 * l.YS) _.base_cards = None for i in range(8): (x, y) = (l.XM + i * l.XS, l.YM) s.reserves.append(ReserveStack(x, y, _, max_accept = 0)) for i in range(8): (x, y) = (l.XM + i * l.XS, l.YM + l.YS) s.foundations.append(Interregnum_Foundation(x, y, _, mod = 13, max_move = 0)) for i in range(rows): (x, y) = (l.XM + (2 * i + 8 - rows) * l.XS / 2, l.YM + 2 * l.YS) s.rows.append(RK_RowStack(x, y, _, max_accept = 0, max_move = 1)) s.talon = DealRowTalonStack(_.width - l.XS, _.height - l.YS, _) l.createText(s.talon, 'nn') l.defaultStackGroups() def startGame(_): _.startDealSample() _.base_cards = [] for i in range(8): _.base_cards.append(_.s.talon.getCard()) _.s.foundations[i].cap.base_rank = (_.base_cards[i].rank + 1) % 13 _.flipMove(_.s.talon) _.moveMove(1, _.s.talon, _.s.reserves[i]) _.s.talon.dealRow() def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def _restoreGameHook(_, game): _.base_cards = [ None] * 8 for i in range(8): id = game.loadinfo.base_card_ids[i] _.base_cards[i] = _.cards[id] _.s.foundations[i].cap.base_rank = (_.base_cards[i].rank + 1) % 13 def _loadGameHook(_, p): ids = [] for i in range(8): ids.append(p.load()) _.loadinfo.addattr(base_card_ids = ids) def _saveGameHook(_, p): for c in _.base_cards: p.dump(c.id) registerGame(GameInfo(172, TamOShanter, "Tam O'Shanter", GI.GT_NUMERICA, 1, 0)) registerGame(GameInfo(95, AuldLangSyne, 'Auld Lang Syne', GI.GT_NUMERICA, 1, 0)) registerGame(GameInfo(173, Strategy, 'Strategy', GI.GT_NUMERICA, 1, 0)) registerGame(GameInfo(123, Interregnum, 'Interregnum', GI.GT_NUMERICA, 2, 0)) class Doublets_Foundation(AbstractFoundationStack): def acceptsCards(_, from_stack, cards): if not AbstractFoundationStack.acceptsCards(_, from_stack, cards): return 0 if _.cards: if (2 * _.cards[-1].rank + 1) % _.cap.mod != cards[0].rank: return 0 return 1 class Doublets(Game): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 5.5 * l.XS, l.YM + 4 * l.YS) for dx, dy in ((0, 0), (1, 0), (2, 0), (0, 1), (2, 1), (0, 2), (2, 2)): (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2) s.rows.append(ReserveStack(x, y, _)) (dx, dy) = (1, 2) (x, y) = (l.XM + (2 * dx + 5) * l.XS / 2, l.YM + (2 * dy + 1) * l.YS / 2) s.foundations.append(Doublets_Foundation(x, y, _, ANY_SUIT, dir = 0, mod = 13, max_move = 0, max_cards = 48)) l.createText(s.foundations[0], 'ss') (x, y) = (l.XM, l.YM + 3 * l.YS / 2) s.talon = WasteTalonStack(x, y, _, max_rounds = 3) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() def _shuffleHook(_, cards): (kings, topcards) = ([], []) for c in cards[:]: cards.remove(c) if c.rank == KING: kings.append(c) else: topcards.append(c) if len(topcards) == 8: break return kings + cards + topcards def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.foundations) _.s.talon.dealCards() def isGameWon(_): if _.s.talon.cards or _.s.waste.cards: return 0 return len(_.s.foundations[0].cards) == 48 def fillStack(_, stack): if stack in _.s.rows and not (stack.cards): old_state = _.enterState(_.S_FILL) if _.s.waste.cards: _.s.waste.moveMove(1, stack) elif _.s.talon.canDealCards(): _.s.talon.dealCards() _.s.waste.moveMove(1, stack) _.leaveState(old_state) def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) registerGame(GameInfo(111, Doublets, 'Doublets', GI.GT_1DECK_TYPE, 1, 2)) class SiebenBisAs_Hint(CautiousDefaultHint): def computeHints(_): game = _.game freerows = filter((lambda s: not (s.cards)), game.s.rows) for r in game.sg.dropstacks: if __debug__: if not len(r.cards) == 1 and r.cards[-1].face_up: raise AssertionError None if not (r.cards) else game.sg.dropstacks (c, pile, rpile) = (r.cards[0], r.cards, []) (t, ncards) = r.canDropCards(_.game.s.foundations) if t: (score, color) = (0, None) (score, color) = _._getDropCardScore(score, color, r, t, ncards) _.addHint(score, ncards, r, t, color) for t in freerows: pass class SiebenBisAs_Foundation(SS_FoundationStack): def acceptsCards(_, from_stack, cards): if not SS_FoundationStack.acceptsCards(_, from_stack, cards): return 0 if not (from_stack in _.game.s.rows): return 0 if from_stack.id % 10 == 0: return 0 return len(_.game.s.rows[from_stack.id - 1].cards) == 0 class SiebenBisAs_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if _.id % 10 != 0: s = _.game.s.rows[_.id - 1] if s.cards and s.cards[-1].suit == cards[0].suit and (s.cards[-1].rank + 1) % 13 == cards[0].rank: return 1 if _.id % 10 != 10 - 1: s = _.game.s.rows[_.id + 1] if s.cards and s.cards[-1].suit == cards[0].suit and (s.cards[-1].rank - 1) % 13 == cards[0].rank: return 1 return 0 def getBottomImage(_): return _.game.app.images.getReserveBottom() class SiebenBisAs(Game): Hint_Class = SiebenBisAs_Hint def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 10 * l.XS, l.YM + 5 * l.YS) for i in range(3): for j in range(10): (x, y) = (l.XM + j * l.XS, l.YM + (i + 1) * l.YS) s.rows.append(SiebenBisAs_RowStack(x, y, _, max_accept = 1, max_cards = 1)) for i in range(2): (x, y) = (l.XM + (i + 4) * l.XS, l.YM) s.reserves.append(ReserveStack(x, y, _, max_accept = 0)) for i in range(4): (x, y) = (l.XM + (i + 3) * l.XS, l.YM + 4 * l.YS) s.foundations.append(SiebenBisAs_Foundation(x, y, _, i, base_rank = 6, mod = 13, max_move = 0)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): _.startDealSample() _.s.talon.dealRow() _.s.talon.dealRow(rows = _.s.reserves) stacks = filter((lambda r: r.cards[-1].rank == 6), _.s.rows) for r in stacks: _.moveMove(1, r, _.s.foundations[r.cards[-1].suit]) class Maze_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 s = _.game.s.rows[(_.id - 1) % 54] if s.cards: if s.cards[-1].suit == cards[0].suit and s.cards[-1].rank + 1 == cards[0].rank: return 1 if s.cards[-1].rank == QUEEN and cards[0].rank == ACE: return 1 s = _.game.s.rows[(_.id + 1) % 54] if s.cards: if s.cards[-1].suit == cards[0].suit and s.cards[-1].rank - 1 == cards[0].rank: return 1 return 0 prepareBottom = Stack.prepareInvisibleBottom def getBottomImage(_): return _.game.app.images.getReserveBottom() class Maze(Game): GAME_VERSION = 2 Hint_Class = SiebenBisAs_Hint def createGame(_): (l, s) = (Layout(_, XM = 4, YM = 4), _.s) _.setSize(l.XM + 9 * l.XS, l.YM + 6 * l.YS) for i in range(6): for j in range(9): (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS) s.rows.append(Maze_RowStack(x, y, _, max_accept = 1, max_cards = 1)) s.talon = InitialDealTalonStack((_.width - l.XS) + 1, _.height - l.YS, _) s.internals.append(InvisibleStack(_)) l.defaultStackGroups() def startGame(_): frames = 0 for i in range(54): c = _.s.talon.cards[-1] if c.rank == KING: _.s.talon.dealRow(rows = _.s.internals, frames = 0) elif frames == 0 and i >= 36: _.startDealSample() frames = -1 _.s.talon.dealRow(rows = (_.s.rows[i],), frames = frames) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError def isGameWon(_): rows = filter((lambda s: s.cards), _.s.rows) if len(rows) != 48: return 0 i = 0 if 1: while rows[i].cards[-1].rank != ACE: i = i + 1 rows = rows + rows for j in (i + 0, i + 12, i + 24, i + 36): r1 = rows[j] r2 = rows[j + 11] if r1.cards[-1].rank != ACE or r2.cards[-1].rank != QUEEN: return 0 pile = getPileFromStacks(rows[j:j + 12]) if not pile or not isSameSuitSequence(pile, dir = 1): return 0 return 1 registerGame(GameInfo(118, SiebenBisAs, 'Sieben bis As', GI.GT_MONTANA, 1, 0, ranks = (0, 6, 7, 8, 9, 10, 11, 12))) registerGame(GameInfo(144, Maze, 'Maze', GI.GT_MONTANA | GI.GT_OPEN, 1, 0, si = { 'ncards': 48 })) class DieBoeseSieben_Talon(DieKoenigsbergerin_Talon): def canDealCards(_): if not len(_.cards): pass return _.round != _.max_rounds def dealCards(_, sound = 0): if _.cards: return DieKoenigsbergerin_Talon.dealCards(_, sound = sound) (game, num_cards) = (_.game, len(_.cards)) for r in game.s.rows: while r.cards: num_cards = num_cards + 1 game.moveMove(1, r, _, frames = 0) continue None if r.cards[-1].face_up else game.s.rows if not __debug__ and len(_.cards) == num_cards: raise AssertionError if sound: game.startDealSample() game.shuffleStackMove(_) game.nextRoundMove(_) n = len(game.s.rows) flip = num_cards / n & 1 while _.cards: if len(_.cards) <= n: flip = 1 _.dealRow(flip = flip) flip = not flip if sound: game.stopSamples() return num_cards class DieBoeseSieben(Game): def createGame(_, rows = 7): (l, s) = (Layout(_), _.s) _.setSize(l.XM + max(8, rows) * l.XS, l.YM + 5 * l.YS) for i in range(8): (x, y) = (l.XM + i * l.XS, l.YM) s.foundations.append(DieRussische_Foundation(x, y, _, i / 2, max_move = 0)) for i in range(rows): (x, y) = (l.XM + (2 * i + 8 - rows) * l.XS / 2, l.YM + l.YS) s.rows.append(AC_RowStack(x, y, _)) s.talon = DieBoeseSieben_Talon(l.XM, _.height - l.YS, _, max_rounds = 2) l.createText(s.talon, 'se') l.defaultStackGroups() def startGame(_): _.startDealSample() for flip in (1, 0, 1, 0, 1, 0, 1): _.s.talon.dealRow(flip = flip) registerGame(GameInfo(120, DieBoeseSieben, 'Die b\xf6se Sieben', GI.GT_2DECK_TYPE, 2, 1, ranks = (0, 6, 7, 8, 9, 10, 11, 12))) class Terrace_Talon(WasteTalonStack): def canDealCards(_): if _.game.getState() == 0: return 0 return WasteTalonStack.canDealCards(_) class Terrace_AC_Foundation(AC_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, mod = 13, min_cards = 1, max_move = 0) apply(AC_FoundationStack.__init__, (_, x, y, game, suit), cap) def acceptsCards(_, from_stack, cards): if _.game.getState() == 0: if len(cards) != 1 or not (cards[0].face_up): return 0 if cards[0].suit != _.cap.base_suit: return 0 return from_stack in _.game.s.rows return AC_FoundationStack.acceptsCards(_, from_stack, cards) class Terrace_SS_Foundation(SS_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, mod = 13, min_cards = 1, max_move = 0) apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap) def acceptsCards(_, from_stack, cards): if _.game.getState() == 0: if len(cards) != 1 or not (cards[0].face_up): return 0 if cards[0].suit != _.cap.base_suit: return 0 return from_stack in _.game.s.rows return SS_FoundationStack.acceptsCards(_, from_stack, cards) class Terrace_RowStack(AC_RowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, mod = 13, max_move = 1) apply(AC_RowStack.__init__, (_, x, y, game), cap) def acceptsCards(_, from_stack, cards): if _.game.getState() == 0: return 0 if from_stack in _.game.s.reserves: return 0 return AC_RowStack.acceptsCards(_, from_stack, cards) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): state = _.game.getState() if state > 0: AC_RowStack.moveMove(_, ncards, to_stack, frames = frames, shadow = shadow) return None if not __debug__ and to_stack in _.game.s.foundations: raise AssertionError if not __debug__ and ncards == 1: raise AssertionError if not __debug__ and not (_.game.s.waste.cards): raise AssertionError _.game.moveMove(ncards, _, to_stack, frames = frames, shadow = shadow) for s in _.game.s.foundations: s.cap.base_rank = to_stack.cards[0].rank freerows = filter((lambda s: not (s.cards)), _.game.s.rows) _.game.s.talon.dealRow(rows = freerows, sound = 1) _.game.s.talon.dealCards() def getBottomImage(_): return _.game.app.images.getReserveBottom() class Terrace(Game): Foundation_Class = Terrace_AC_Foundation RowStack_Class = Terrace_RowStack ReserveStack_Class = OpenStack Hint_Class = CautiousDefaultHint INITIAL_RESERVE_CARDS = 11 def createGame(_, rows = 9, max_rounds = 1, num_deal = 1): (l, s) = (Layout(_, XOFFSET = 14), _.s) maxrows = max(rows, 9) (w1, w2) = ((maxrows - 8) * l.XS / 2, (maxrows - rows) * l.XS / 2) h = max(3 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + maxrows * l.XS + l.XM, l.YM + 2 * l.YS + h) _.base_card = None (x, y) = (l.XM + w1, l.YM) s.talon = Terrace_Talon(x, y, _, max_rounds = max_rounds, num_deal = num_deal) l.createText(s.talon, 'sw') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se', text_format = '%D') x = x + 2 * l.XS stack = _.ReserveStack_Class(x, y, _) stack.CARD_XOFFSET = l.XOFFSET l.createText(stack, 'sw') s.reserves.append(stack) (x, y) = (l.XM + w1, y + l.YS) for i in range(8): s.foundations.append(_.Foundation_Class(x, y, _, suit = i / 2)) x = x + l.XS (x, y) = (l.XM + w2, y + l.YS) for i in range(rows): s.rows.append(_.RowStack_Class(x, y, _)) x = x + l.XS l.defaultStackGroups() def getState(_): for s in _.s.foundations: pass return 0 def startGame(_): _.startDealSample() for i in range(_.INITIAL_RESERVE_CARDS): _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealRow(rows = _.s.rows[:4]) def fillStack(_, stack): if not (stack.cards): old_state = _.enterState(_.S_FILL) if stack is _.s.waste and _.s.talon.cards: _.s.talon.dealCards() elif stack in _.s.rows and _.s.waste.cards: _.s.waste.moveMove(1, stack) _.leaveState(old_state) def _restoreGameHook(_, game): for s in _.s.foundations: s.cap.base_rank = game.loadinfo.base_rank def _loadGameHook(_, p): _.loadinfo.addattr(base_rank = p.load()) def _saveGameHook(_, p): base_rank = NO_RANK for s in _.s.foundations: pass p.dump(base_rank) class GeneralsPatience(Terrace): Foundation_Class = Terrace_SS_Foundation INITIAL_RESERVE_CARDS = 13 class BlondesAndBrunettes(Terrace): INITIAL_RESERVE_CARDS = 10 def startGame(_): _.startDealSample() for i in range(_.INITIAL_RESERVE_CARDS): _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealRow() c = _.s.talon.getCard() for s in _.s.foundations: s.cap.base_rank = c.rank _.s.talon.dealRow(rows = (_.s.foundations[2 * c.suit],)) _.s.talon.dealCards() def getState(_): return 1 class FallingStar(BlondesAndBrunettes): INITIAL_RESERVE_CARDS = 11 registerGame(GameInfo(135, Terrace, 'Terrace', GI.GT_TERRACE, 2, 0, altnames = 'Queen of Italy')) registerGame(GameInfo(136, GeneralsPatience, "General's Patience", GI.GT_TERRACE, 2, 0)) registerGame(GameInfo(137, BlondesAndBrunettes, 'Blondes and Brunettes', GI.GT_TERRACE, 2, 0)) registerGame(GameInfo(138, FallingStar, 'Falling Star', GI.GT_TERRACE, 2, 0)) class PokerSquare_RowStack(ReserveStack): def clickHandler(_, event): if not (_.cards): _.game.s.talon.playMoveMove(1, _) return 1 return ReserveStack.clickHandler(_, event) rightclickHandler = clickHandler class PokerSquare(Game): Talon_Class = OpenTalonStack RowStack_Class = StackWrapper(PokerSquare_RowStack, max_move = 0) Hint_Class = None WIN_SCORE = 100 def createGame(_): (l, s) = (Layout(_), _.s) ta = 'ss' (x, y) = (l.XM, l.YM + 2 * l.YS) if _.preview <= 1: t = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = 'Royal Flush\nStraight Flush\nFour of a Kind\n' + 'Full House\nFlush\nStraight\n' + 'Three of a Kind\nTwo Pair\nOne Pair') bb = t.bbox() x = bb[2] + 16 h = bb[3] - bb[1] if h >= 2 * l.YS: ta = 'e' t.move(0, -(l.YS)) y = y - l.YS t = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = '100\n75\n50\n25\n20\n15\n10\n5\n2') x = t.bbox()[2] + 16 _.texts.misc = MfxCanvasText(_.canvas, x, y, anchor = 'nw', text = '0\n' * 8 + '0') x = _.texts.misc.bbox()[2] + 32 w = max(2 * l.XS, x) _.setSize(l.XM + w + 5 * l.XS + 50, l.YM + 5 * l.YS + 30) for i in range(5): for j in range(5): (x, y) = (l.XM + w + j * l.XS, l.YM + i * l.YS) s.rows.append(_.RowStack_Class(x, y, _)) (x, y) = (l.XM, l.YM) s.talon = _.Talon_Class(x, y, _) l.createText(s.talon, anchor = ta) s.internals.append(InvisibleStack(_)) r = s.rows _.poker_hands = [ r[0:5], r[5:10], r[10:15], r[15:20], r[20:25], (r[0], r[0 + 5], r[0 + 10], r[0 + 15], r[0 + 20]), (r[1], r[1 + 5], r[1 + 10], r[1 + 15], r[1 + 20]), (r[2], r[2 + 5], r[2 + 10], r[2 + 15], r[2 + 20]), (r[3], r[3 + 5], r[3 + 10], r[3 + 15], r[3 + 20]), (r[4], r[4 + 5], r[4 + 10], r[4 + 15], r[4 + 20])] _.poker_hands = map(tuple, _.poker_hands) l.defaultStackGroups() return l def startGame(_): _.moveMove(27, _.s.talon, _.s.internals[0], frames = 0) _.s.talon.fillStack() def isGameWon(_): if len(_.s.talon.cards) == 0: pass return _.getGameScore() >= _.WIN_SCORE def getAutoStacks(_, event = None): return ((), (), ()) def updateText(_): if _.preview > 1: return None score = 0 count = [ 0] * 9 for i in range(10): (type, value) = _.getHandScore(_.poker_hands[i]) if type <= type: pass elif type <= 8: count[type] = count[type] + 1 _.texts.hands[i].config(text = str(value)) score = score + value t = string.join(map(str, count), '\n') _.texts.misc.config(text = t) t = '' if score >= _.WIN_SCORE: t = 'WON\n\n' if _.s.talon.cards: t = t + 'Points: %d' % score else: t = t + 'Total: %d' % score _.texts.score.config(text = t) def getGameScore(_): score = 0 for hand in _.poker_hands: (type, value) = _.getHandScore(hand) score = score + value return score def getHandScore(_, hand): same_rank = [ 0] * 13 same_suit = [ 0] * 4 ranks = [] for s in hand: pass straight = 0 if same_rank.count(1) == 5: d = max(ranks) - min(ranks) if d == 4: straight = 1 elif d == 12 and same_rank[-4:].count(1) == 4: straight = 2 if max(same_suit) == 5: if straight: if straight == 2: return (0, 100) return (1, 75) return (4, 20) if straight: return (5, 15) if max(same_rank) >= 2: same_rank.sort() if same_rank[-1] == 4: return (2, 50) if same_rank[-1] == 3: if same_rank[-2] == 2: return (3, 25) return (6, 10) if same_rank[-2] == 2: return (7, 5) return (8, 2) return (-1, 0) class PokerShuffle_RowStack(ReserveStack): def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): if __debug__: if not ncards == 1 and to_stack in _.game.s.rows: raise AssertionError if not __debug__ and len(to_stack.cards) == 1: raise AssertionError _._swapPairMove(ncards, to_stack, frames = -1, shadow = 0) def _swapPairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game old_state = game.enterState(game.S_FILL) swap = game.s.internals[0] game.moveMove(n, _, swap, frames = 0) game.moveMove(n, other_stack, _, frames = frames, shadow = shadow) game.moveMove(n, swap, other_stack, frames = 0) game.leaveState(old_state) class PokerShuffle(PokerSquare): Talon_Class = InitialDealTalonStack RowStack_Class = StackWrapper(PokerShuffle_RowStack, max_accept = 1, max_cards = 2) WIN_SCORE = 200 def createGame(_): l = PokerSquare.createGame(_) if _.s.talon.texts.ncards: _.s.talon.texts.ncards.text_format = '%D' def startGame(_): _.moveMove(27, _.s.talon, _.s.internals[0], frames = 0) _.startDealSample() _.s.talon.dealRow() def checkForWin(_): return 0 registerGame(GameInfo(139, PokerSquare, 'Poker Square', GI.GT_POKER_TYPE | GI.GT_SCORE, 1, 0, si = { 'ncards': 25 })) registerGame(GameInfo(140, PokerShuffle, 'Poker Shuffle', GI.GT_POKER_TYPE | GI.GT_SCORE | GI.GT_OPEN, 1, 0, si = { 'ncards': 25 })) class DerKatzenschwanz(Game): RowStack_Class = StackWrapper(AC_RowStack, base_rank = NO_RANK) def createGame(_, rows = 9, reserves = 8): (l, s) = (Layout(_, XOFFSET = 10), _.s) maxrows = max(rows, reserves) _.setSize(l.XM + (maxrows + 2) * l.XS, l.YM + 6 * l.YS) playcards = 4 * l.YS / l.YOFFSET (xoffset, yoffset) = ([], []) for i in range(playcards): xoffset.append(0) yoffset.append(l.YOFFSET) for i in range(104 - playcards): xoffset.append(l.XOFFSET) yoffset.append(0) (x, y) = (l.XM + (maxrows - reserves) * l.XS / 2, l.YM) for i in range(reserves): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS (x, y) = (l.XM + (maxrows - rows) * l.XS / 2, l.YM + l.YS) _.setRegion(s.reserves, (-999, -999, 999999, y - l.XM / 2)) for i in range(rows): stack = _.RowStack_Class(x, y, _) stack.CARD_XOFFSET = xoffset stack.CARD_YOFFSET = yoffset s.rows.append(stack) x = x + l.XS (x, y) = (l.XM + maxrows * l.XS, l.YM) for suit in range(4): for i in range(2): s.foundations.append(SS_FoundationStack(x + i * l.XS, y, _, suit = suit)) y = y + l.YS _.setRegion(_.s.foundations, (x - l.CW / 2, -999, 999999, y), priority = 1) s.talon = InitialDealTalonStack(_.width - 3 * l.XS / 2, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): _.startDealSample() i = 0 while _.s.talon.cards: if _.s.talon.cards[-1].rank == KING: if _.s.rows[i].cards: i = i + 1 _.s.talon.dealRow(rows = [ _.s.rows[i]], frames = 4) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.color != card2.color and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def _getClosestStack(_, cx, cy, stacks, dragstack): (closest, cdist) = (None, 999999999) for stack in stacks: if dist < cdist: (closest, cdist) = (stack, dist) return closest class DieSchlange(DerKatzenschwanz): RowStack_Class = StackWrapper(FreeCell_AC_RowStack, base_rank = NO_RANK) Hint_Class = FreeCellType_Hint def createGame(_): DerKatzenschwanz.createGame(_, rows = 9, reserves = 7) def startGame(_): _.startDealSample() i = 0 while _.s.talon.cards: c = _.s.talon.cards[-1] if c.rank == ACE: to_stack = _.s.foundations[c.suit * 2] if to_stack.cards: to_stack = _.s.foundations[c.suit * 2 + 1] elif c.rank == KING and _.s.rows[i].cards: i = i + 1 to_stack = _.s.rows[i] _.s.talon.dealRow(rows = (to_stack,), frames = 4) registerGame(GameInfo(141, DerKatzenschwanz, 'Der Katzenschwanz', GI.GT_FREECELL | GI.GT_OPEN, 2, 0)) registerGame(GameInfo(142, DieSchlange, 'Die Schlange', GI.GT_FREECELL | GI.GT_OPEN, 2, 0)) class Napoleon_Talon(InitialDealTalonStack): pass class Napoleon_Foundation(Braid_Foundation): pass class Napoleon_RowStack(BasicRowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, mod = 13, max_move = 1, max_accept = 1) apply(BasicRowStack.__init__, (_, x, y, game), cap) def acceptsCards(_, from_stack, cards): if not OpenStack.acceptsCards(_, from_stack, cards): return 0 if not (_.cards): return 1 (c1, c2) = (_.cards[-1], cards[0]) if c1.suit != c2.suit: return 0 if not c1.rank == (c2.rank + 1) % _.cap.mod: pass return c2.rank == (c1.rank + 1) % _.cap.mod def getBottomImage(_): return _.game.app.images.getReserveBottom() class Napoleon_ReserveStack(BasicRowStack): def __init__(_, x, y, game, **cap): kwdefault(cap, max_move = 1, max_accept = 0) apply(BasicRowStack.__init__, (_, x, y, game), cap) class Napoleon_SingleFreeCell(ReserveStack): def acceptsCards(_, from_stack, cards): return ReserveStack.acceptsCards(_, from_stack, cards) def canMoveCards(_, cards): if _.game.s.rows[8].cards and _.game.s.rows[9].cards: return 0 return ReserveStack.canMoveCards(_, cards) class Napoleon_FreeCell(ReserveStack): def canMoveCards(_, cards): if _.game.s.rows[_.id - 2].cards: return 0 return ReserveStack.canMoveCards(_, cards) class DerKleineNapoleon(Game): def createGame(_, reserves = 1): (l, s) = (Layout(_), _.s) _.setSize(l.XM + 2 * 24 + 2 * l.XM + 11 * l.XS, l.YM + 5 * l.YS + 2 * l.XM) x0 = l.XM + 24 + 4 * l.XS x1 = x0 + l.XS + l.XM x2 = x1 + l.XS + l.XM y = l.YM for i in range(4): s.rows.append(Napoleon_RowStack(x0, y, _)) s.rows.append(Napoleon_RowStack(x2, y, _)) y = y + l.YS y = _.height - l.YS (x, y) = (x1, l.YM) for i in range(4): s.foundations.append(Napoleon_Foundation(x, y, _, i)) y = y + l.YS s.talon = Napoleon_Talon(x, y, _) for r in s.rows: r.CARD_YOFFSET = 0 l.defaultStackGroups() def _shuffleHook(_, cards): rank = cards[-1].rank return _._shuffleHookMoveToBottom(cards, (lambda c, rank = rank: (c.rank == rank, c.suit))) def startGame(_): for i in range(4): _.s.talon.dealRow(rows = _.s.rows[:8], frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[:8]) for i in range(4): _.s.talon.dealRow(rows = _.s.rows[8:]) _.s.talon.dealBaseCards(ncards = 4) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 13 == card2.rank: pass return (card2.rank + 1) % 13 == card1.rank def updateText(_): if _.preview > 1 or not (_.texts.info): return None t = '' f = _.s.foundations[0] if f.cards: t = RANKS[f.cards[0].rank] dir = _.getFoundationDir() if dir == 1: t = t + ' Ascending' elif dir == -1: t = t + ' Descending' _.texts.info.config(text = t) class DerFreieNapoleon(DerKleineNapoleon): def createGame(_, reserves = 1): (l, s) = (Layout(_), _.s) h = l.CH * 2 / 3 + (15 - 1) * l.YOFFSET h = l.YS + max(h, 3 * l.YS) _.setSize(l.XM + 2 * l.XM + 10 * l.XS, l.YM + h) x1 = l.XM + 8 * l.XS + 2 * l.XM y = l.YM + l.YS for j in range(8): x = l.XM + j * l.XS s.rows.append(Napoleon_RowStack(x, y, _)) for j in range(2): x = x1 + j * l.XS s.rows.append(Napoleon_ReserveStack(x, y, _)) _.setRegion(s.rows, (-999, y - l.YM / 2, 999999, 999999)) y = l.YM x = l.XM + 2 * l.XS for i in range(4): s.foundations.append(Napoleon_Foundation(x, y, _, i)) x = x + l.XS (tx, ty, ta, tf) = l.getTextAttr(s.foundations[-1], 'se') font = getFont('canvas_card', cardw = l.CW) _.texts.info = MfxCanvasText(_.canvas, tx + l.XM, ty, anchor = ta, font = font) (x, y) = (l.XM, _.height - l.YS) s.talon = Napoleon_Talon(x, y, _) l.defaultStackGroups() class Napoleon(DerKleineNapoleon): def createGame(_): DerKleineNapoleon.createGame(_, reserves = 2) class FreeNapoleon(DerFreieNapoleon): def createGame(_): DerFreieNapoleon.createGame(_, reserves = 2) registerGame(GameInfo(167, DerKleineNapoleon, 'Der kleine Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(168, DerFreieNapoleon, 'Der freie Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(169, Napoleon, 'Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0)) registerGame(GameInfo(170, FreeNapoleon, 'Free Napoleon', GI.GT_NAPOLEON | GI.GT_OPEN, 1, 0)) class LarasGame_Hint(CautiousDefaultHint): pass class LarasGame_Talon(WasteTalonStack): def dealRow(_, rows = None, flip = 1, reverse = 0, frames = -1): if rows is None: rows = _.game.s.rows old_state = _.game.enterState(_.game.S_DEAL) temp = _.dealToStacks(rows, flip, reverse, frames) if len(_.cards) > 2: _.game.moveMove(1, _, _.game.s.waste, frames = frames) _.game.moveMove(1, _, _.game.s.waste, frames = frames) _.game.leaveState(old_state) return temp + 2 def dealToStacks(_, stacks, flip = 1, reverse = 0, frames = -1): if len(_.cards) == 0: return 0 for r in stacks: if not __debug__ and not (_.getCard().face_up): raise AssertionError None if len(_.cards) == 0 else stacks if not __debug__ and r is not _: raise AssertionError if flip: _.game.flipMove(_) _.game.moveMove(1, _, r, frames = frames) if r.getCard().rank == r.id: if len(_.cards) == 0: return 0 _.game.moveMove(1, _, _.game.s.waste, frames = frames) if r.getCard().rank == ACE: if len(_.cards) < 2: return 0 _.game.moveMove(1, _, _.game.s.waste, frames = frames) _.game.moveMove(1, _, _.game.s.waste, frames = frames) if r.getCard().rank == JACK and r.getCard().rank == QUEEN or r.getCard().rank == KING: if len(_.cards) == 0: return 0 _.game.moveMove(1, _, _.game.s.waste, frames = frames) return len(stacks) def dealCards(_, sound = 0): num_cards = 0 waste = _.game.s.waste if _.cards: curr_rank = _.getCard().rank for i in range(_.game.NUMRESERVES): pass _.game.flipMove(_) _.game.moveMove(1, _, _.game.s.reserves[0], frames = 4, shadow = 0) res_begin = len(_.game.s.rows[curr_rank].cards) + 1 for i in range(len(_.game.s.rows[curr_rank].cards)): _.game.moveMove(1, _.game.s.rows[curr_rank], _.game.s.reserves[res_begin - (i + 1)], frames = 4, shadow = 0) _.game.old_rank = curr_rank elif waste.cards and _.round != _.max_rounds: num_cards = len(waste.cards) _.game.turnStackMove(waste, _, update_flags = 1) return num_cards class LarasGame_RowStack(OpenStack): def __init__(_, x, y, game, **cap): apply(OpenStack.__init__, (_, x, y, game), cap) _.CARD_YOFFSET = 1 class LarasGame_ReserveStack(OpenStack): pass class LarasGame(Game): Hint_Class = LarasGame_Hint NUMRESERVES = 20 def createGame(_): (l, s) = (Layout(_, XOFFSET = 10), _.s) _.setSize(l.XM + _.NUMRESERVES * l.XS / 2, l.YM + 6 * l.YS) _.old_rank = 0 x = l.XM + l.XS y = l.YM + l.YS for i in range(13): s.rows.append(LarasGame_RowStack(x, y, _, max_move = 1)) x = x + l.XS x = l.XM y = l.YM for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, dir = -1, base_rank = KING)) y = y + l.YS x = l.XM + l.XS + l.XS y = l.YM for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i)) x = x + l.XS for i in range(2): (x, y) = (l.XM, l.YM + (5 - i) * l.YS) for i in range(_.NUMRESERVES / 2): s.reserves.append(LarasGame_ReserveStack(x, y, _)) x = x + l.XS x = l.XM + 5 * l.XS y = l.YM + 3 * l.YS s.talon = LarasGame_Talon(x, y, _, max_rounds = 1) l.createText(s.talon, 'se') x = x - l.XS s.waste = WasteStack(x, y, _) _.sg.openstacks = s.foundations + s.rows _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.dropstacks = s.rows + s.reserves + [ s.waste] def startGame(_): frames = 0 for i in range(8): if i == 4: _.startDealSample() frames = 4 _.s.talon.dealRow(frames = frames) _.moveMove(len(_.s.waste.cards), _.s.waste, _.s.talon, frames = 0) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getHighlightPilesStacks(_): return () def _autoDeal(_, sound = 1): return 0 def canUndo(_): return 0 def canSaveGame(_): return 0 def saveGame(_, filename, binmode = 1): _.notYetImplemented() def _restoreGameHook(_, game): _.old_rank = game.loadinfo.old_rank def _loadGameHook(_, p): _.loadinfo.addattr(old_rank = 0) _.loadinfo.old_rank = p.load() def _saveGameHook(_, p): p.dump(_.old_rank) registerGame(GameInfo(37, LarasGame, "Lara's Game", GI.GT_2DECK_TYPE | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0)) class Sanibel(Gypsy): Layout_Method = Layout.klondikeLayout Talon_Class = StackWrapper(WasteTalonStack, max_rounds = 1) Foundation_Class = StackWrapper(SS_FoundationStack, max_move = 0) RowStack_Class = Yukon_AC_RowStack Hint_Class = Yukon_Hint def createGame(_): Gypsy.createGame(_, rows = 10, waste = 1, playcards = 23) def startGame(_): for i in range(3): _.s.talon.dealRow(flip = 0, frames = 0) for i in range(6): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def getHighlightPilesStacks(_): return () registerGame(GameInfo(201, Sanibel, 'Sanibel', GI.GT_YUKON | GI.GT_CONTRIB | GI.GT_ORIGINAL, 2, 0)) class Flower_FoundationStack(AbstractFoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, max_cards = 12, max_move = 0, base_rank = ANY_RANK) apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap) def isOonsooSequence(_, s): for i in range(len(s) - 1): if s[i].rank == 10: (a, b) = (s[i].suit, s[i + 1].suit) if b == 0: b = 4 else: (a, b) = _.swapTrashCards(s[i], s[i + 1]) if a + 1 != b: return 0 return cardsFaceUp(s) def swapTrashCards(_, carda, cardb): (a, b) = (carda.suit, cardb.suit) if a == 0 and b == 3 and a == 3 and b == 0: (a, b) = (0, 1) elif a == 2 and b == 0 and a == 1 and b == 0: b = 3 return (a, b) class FlowerClock_Foundation(Flower_FoundationStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return cards[0].suit == 0 if not (cards[0].rank == stackcards[-1].rank): return 0 i = cards[0].suit j = stackcards[-1].suit if j == 0: j = 4 return i + 1 == j class Gaji_Foundation(Flower_FoundationStack): def __init__(_, x, y, game, suit, **cap): apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap) _.CARD_YOFFSET = _.game.app.images.CARD_YOFFSET def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if stackcards[-1].suit == cards[0].suit: pass return (stackcards[-1].rank + 1) % 12 == cards[0].rank class Pagoda_Foundation(Flower_FoundationStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: if cards[0].suit == 0: pass return cards[0].rank == _.id if not (cards[0].rank == stackcards[-1].rank): return 0 i = cards[0].suit j = stackcards[-1].suit if j == 0: j = 4 if len(stackcards) < 4: return i == j - 1 elif len(stackcards) > 4: if i == 0: i = 4 return i == j + 1 else: return i == j class Samuri_Foundation(Flower_FoundationStack): def __init__(_, x, y, game, suit, **cap): apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap) _.CARD_YOFFSET = -(_.game.app.images.CARD_YOFFSET) def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: if cards[0].suit == 0: pass return cards[0].rank == _.id i = stackcards[-1].suit if i == 0: i = 4 if cards[0].rank == stackcards[-1].rank: pass return cards[0].suit == i - 1 def getBottomImage(_): return _.game.app.images.getTalonBottom() class MatsuKiri_Foundation(Flower_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, max_cards = 48, min_accept = 4, max_accept = 4) apply(AbstractFoundationStack.__init__, (_, x, y, game, suit), cap) _.CARD_YOFFSET = _.game.app.images.CARDH / 10 def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not _.isOonsooSequence(cards): return 0 if not stackcards: if cards[0].suit == 1: pass return cards[0].rank == 0 return stackcards[-1].rank + 1 == cards[0].rank def getBottomImage(_): return _.game.app.images.getLetter(0) class GreatWall_Foundation(Flower_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, max_cards = 36, min_accept = 12, max_accept = 12) apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap) _.CARD_YOFFSET = 0 def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if cards[0].rank != 0: return 0 for i in range(12): pass return isRankSequence(cards, dir = 1) class GreatWall_BuildStack(Flower_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, max_cards = 12, max_move = 12) apply(Flower_FoundationStack.__init__, (_, x, y, game, suit), cap) _.CARD_YOFFSET = _.game.app.images.CARDH / 9 def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if stackcards: if cards[0].rank != stackcards[-1].rank + 1: return 0 elif cards[0].rank != 0: return 0 for c in cards: pass return isRankSequence(cards, dir = 1) class FourWinds_Foundation(Flower_FoundationStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if cards[0].suit != _.id: return 0 if not stackcards: return cards[0].rank == 0 else: return stackcards[-1].rank + 1 == cards[0].rank class Flower_OpenStack(OpenStack): def __init__(_, x, y, game, yoffset, **cap): kwdefault(cap, max_move = 999999, max_accept = 999999) apply(OpenStack.__init__, (_, x, y, game), cap) _.CARD_YOFFSET = yoffset def isOonsooSequence(_, s): for i in range(len(s) - 1): if s[i].rank == 10: (a, b) = (s[i].suit, s[i + 1].suit) if b == 0: b = 4 else: (a, b) = _.swapTrashCards(s[i], s[i + 1]) if a + 1 != b: return 0 return 1 def swapTrashCards(_, carda, cardb): (a, b) = (carda.suit, cardb.suit) if a == 0 and b == 3 and a == 3 and b == 0: (a, b) = (3, 4) elif a == 2 and b == 0 and a == 0 and b == 1: b = 3 return (a, b) class FlowerClock_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return 1 return (stackcards[-1].suit + 1) % 4 == cards[0].suit class Gaji_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return 1 elif cards[0].suit == 0 and cards[0].rank == 10 and stackcards[-1].suit == 0 and stackcards[-1].rank == 10: return 1 elif cards[0].rank != stackcards[-1].rank: return 0 (a, b) = _.swapTrashCards(stackcards[-1], cards[0]) return a + 1 == b class Matsukiri_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return cards[0].suit == 1 return _.isOonsooSequence([ stackcards[-1], cards[0]]) class Oonsoo_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not _.isOonsooSequence(cards): return 0 if not stackcards: return cards[0].suit == 1 if stackcards[-1].rank != cards[0].rank: return 0 return _.isOonsooSequence([ stackcards[-1], cards[0]]) class Samuri_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return cards[0].suit == 1 i = cards[0].suit if i == 0: i = 4 if stackcards[-1].rank == cards[0].rank: pass return stackcards[-1].suit == i - 1 class GreatWall_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return cards[0].suit == 1 if cards[0].suit == stackcards[-1].suit: return (cards[0].rank + 1) % 12 == stackcards[-1].rank (a, b) = _.swapTrashCards(stackcards[-1], cards[0]) return a + 1 == b class FourWinds_RowStack(Flower_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if not stackcards: return 1 if cards[0].suit == stackcards[-1].suit: pass return cards[0].rank + 1 == stackcards[-1].rank def getBottomImage(_): return _.game.app.images.getReserveBottom() class AbstractFlowerGame(Game): SUITS = ('Pine', 'Plum', 'Cherry', 'Wisteria', 'Iris', 'Rose', 'Clover', 'Moon', 'Mum', 'Maple', 'Rain', 'Phoenix') def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank != card2.rank: return 0 (a, b) = (card1.suit, card2.suit) if a == 0: a = 4 elif b == 0: b = 4 if not a + 1 == b: pass return a - 1 == b class FlowerClock(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 10.5, l.YM + l.YS * 5.5) xoffset = (1, 2, 2.5, 2, 1, 0, -1, -2, -2.5, -2, -1, 0) yoffset = (0.25, 0.75, 1.9, 3, 3.5, 3.75, 3.5, 3, 1.9, 0.75, 0.25, 0) x = l.XM + l.XS * 7 y = l.CH / 3 for i in range(12): x0 = x + xoffset[i] * l.XS y0 = y + yoffset[i] * l.YS s.foundations.append(FlowerClock_Foundation(x0, y0, _, ANY_SUIT)) t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS, anchor = 'center', font = font, text = _.SUITS[i]) for j in range(2): (x, y) = (l.XM, l.YM + l.YS * j * 2.7) for i in range(4): s.rows.append(FlowerClock_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 8)) x = x + l.XS _.setRegion(s.rows, (0, 0, l.XS * 4, 999999)) s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def isGameWon(_): for i in _.s.foundations: if i.cards[0].rank != i.id: return 0 return 1 def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 def getAutoStacks(_, event = None): if event is None: return (_.sg.dropstacks, (), _.sg.dropstacks) else: return (_.sg.dropstacks, _.sg.dropstacks, _.sg.dropstacks) class Gaji(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM * 7 + l.XS * 12, l.YM * 2 + l.YS * 6) x = l.XM y = l.YM s.foundations.append(Gaji_Foundation(x, y, _, 1)) x = x + l.XS + 10 s.foundations.append(Gaji_Foundation(x, y, _, 2)) x = x + l.XS + 20 for i in range(8): s.rows.append(Gaji_RowStack(x, y, _, yoffset = l.CH / 2, max_cards = 12)) x = x + l.XS _.setRegion(s.rows, (l.XM + l.XS * 2 + 11, -999, l.XM + l.XS * 10 + 20, 999999)) x = x + 20 s.foundations.append(Gaji_Foundation(x, y, _, 3)) x = x + l.XS + 10 s.foundations.append(Gaji_Foundation(x, y, _, 0)) s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _) l.defaultStackGroups() def _shuffleHook(_, cards): topcards = [ None, None, None, None] for c in cards[:]: if not topcards[c.suit]: if c.rank == 10: pass if not (c.suit == 0): topcards[c.suit] = c cards.remove(c) return topcards + cards def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError for i in range(4): _.s.talon.dealRow(frames = 0) _.startDealSample() r = _.s.rows _.s.talon.dealRow(rows = (r[0], r[1], r[2], r[5], r[6], r[7])) _.s.talon.dealRow(rows = (r[0], r[1], r[6], r[7])) _.s.talon.dealRow(rows = (r[0], r[7])) r = _.s.foundations _.s.talon.dealRow(rows = (r[2], r[1], r[0], r[3])) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def fillStack(_, stack): if stack in _.s.rows: if stack.cards and not (stack.cards[-1].face_up): _.flipMove(stack) class Oonsoo(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM * 2 + l.XS * 8, l.YM + l.YS * 6) x = l.XM + l.XS * 1.5 y = l.YM for i in range(6): s.rows.append(Oonsoo_RowStack(x, y, _, yoffset = l.YOFFSET)) x = x + l.XS * 1.1 y = y + l.YS * 2.5 x = x - l.XS * 6.6 for i in range(6): s.rows.append(Oonsoo_RowStack(x, y, _, yoffset = l.YOFFSET)) x = x + l.XS * 1.1 x = l.XM y = l.YM s.talon = DealRowTalonStack(x, y, _) l.createText(s.talon, 'ss') l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() _.s.talon.dealCards() def isGameWon(_): if _.s.talon.cards: return 0 for s in _.s.rows: pass return 1 def fillStack(_, stack): if stack in _.s.rows: if stack.cards and not (stack.cards[-1].face_up): _.flipMove(stack) class Pagoda(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 11, l.YS * 6) _.foundation_texts = [] for j in range(4): (x, y) = (l.XM + l.XS * 8, l.YM * 3 + l.YS * j * 1.5) for i in range(3): s.foundations.append(Pagoda_Foundation(x, y, _, ANY_SUIT)) t = MfxCanvasText(_.canvas, x + l.CW / 2, y - 12, anchor = 'center', font = font) _.foundation_texts.append(t) x = x + l.XS (x, y) = (l.XM + l.XS, l.YM) d = (0.4, 0.25, 0, 0.25, 0.4) for i in range(5): s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _)) (x, y) = (l.XM + l.XS * 2, y + l.YS * 1.1) d = (0.25, 0, 0.25) for i in range(3): s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _)) (x, y) = (l.XM, y + l.YS * 1.1) d = (0.5, 0.4, 0.25, 0, 0.25, 0.4, 0.5) for i in range(7): s.reserves.append(ReserveStack(x + l.XS * i, y + l.YS * d[i], _)) (x, y) = (l.XM + l.XS, y + l.YS * 1.5) for i in range(5): s.reserves.append(ReserveStack(x + l.XS * i, y, _)) x = l.XM + l.XS * 2.5 y = l.YM + l.YS * 4.9 s.talon = WasteTalonStack(x, y, _, num_deal = 4, max_rounds = 1) l.createText(s.talon, 'sw') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se') l.defaultStackGroups() def updateText(_): if _.preview > 1: return None for i in range(12): s = _.s.foundations[i] if not (s.cards) or len(s.cards) == 8: text = _.SUITS[i] elif len(s.cards) < 5: text = 'Rising' else: text = 'Setting' _.foundation_texts[i].config(text = text) def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48 * 2: raise AssertionError _.updateText() _.startDealSample() _.s.talon.dealRow(rows = _.s.reserves) _.s.talon.dealCards() def fillStack(_, stack): if not (stack.cards) and stack is _.s.waste: if _.canDealCards(): _.dealCards() class MatsuKiri(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) _.setSize(l.XM * 3 + l.XS * 9, l.YM + l.YS * 6) x = l.XM y = l.YM for i in range(8): s.rows.append(Matsukiri_RowStack(x, y, _, yoffset = l.CH / 2, max_cards = 12)) x = x + l.XS _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8 + 10, 999999)) x = x + l.XM * 2 s.foundations.append(MatsuKiri_Foundation(x, y, _, ANY_SUIT)) _.setRegion(s.foundations, (l.XM + l.XS * 8 + 10, -999, 999999, 999999)) s.talon = InitialDealTalonStack(_.width - l.XS, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError for i in range(5): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def fillStack(_, stack): if stack in _.s.rows: if len(stack.cards) > 0 and not (stack.cards[-1].face_up): _.flipMove(stack) class Samuri(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM * 3 + l.XS * 11, l.YM + l.YS * 6) n = 0 for i in range(3): x = l.XM y = (l.YS - l.YM) + l.YS * i * 2 for j in range(2): s.foundations.append(Samuri_Foundation(x, y, _, ANY_SUIT)) t = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS + 5, anchor = 'center', font = font, text = _.SUITS[n]) x = x + l.XS n = n + 1 for i in range(3): x = l.XM * 3 + l.XS * 9 y = (l.YS - l.YM) + l.YS * i * 2 for j in range(2): s.foundations.append(Samuri_Foundation(x, y, _, ANY_SUIT)) t = MfxCanvasText(_.canvas, x + l.CW / 2, y + l.YS + 5, anchor = 'center', font = font, text = _.SUITS[n]) x = x + l.XS n = n + 1 x = l.XM * 2 + l.XS * 2 y = l.YM for i in range(7): s.rows.append(Samuri_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 8)) x = x + l.XS _.setRegion(s.rows, (l.XM + l.XS * 2, -999, l.XM * 2 + l.XS * 9, 999999)) x = l.XM * 2 + l.XS * 4.5 y = _.height - l.YS * 2 s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 1) l.createText(s.talon, 'sw') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'se') l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError _.s.talon.dealRow(flip = 0, frames = 0) for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:7 - i], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def fillStack(_, stack): if not (stack.cards) and stack is _.s.waste: if _.canDealCards(): _.dealCards() if stack in _.s.rows: if len(stack.cards) and not (stack.cards[-1].face_up): _.flipMove(stack) class GreatWall(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 15, l.YM + l.YS * 6.2) _.foundation_texts = [] x = l.XM y = l.YM for i in range(2): s.foundations.append(GreatWall_Foundation(x, y, _, i + 1)) _.foundation_texts.append(MfxCanvasText(_.canvas, x + l.CW + 4, y + l.CH / 2, anchor = 'w', font = font)) y = y + l.YS * 1 x = _.width - l.XS y = l.YM for i in range(2): s.foundations.append(GreatWall_Foundation(x, y, _, (i + 3) % 4)) _.foundation_texts.append(MfxCanvasText(_.canvas, x - 4, y + l.CH / 2, anchor = 'e', font = font)) y = y + l.YS * 1 x = l.XM y = l.YM + l.YS * 2.2 for i in range(2): s.foundations.append(GreatWall_BuildStack(x, y, _, i + 1)) _.foundation_texts.append(MfxCanvasText(_.canvas, x + l.CW + 4, y + l.CH / 2, anchor = 'w', font = font)) y = y + l.YS * 2 x = _.width - l.XS y = l.YM + l.YS * 2.2 for i in range(2): s.foundations.append(GreatWall_BuildStack(x, y, _, (i + 3) % 4)) _.foundation_texts.append(MfxCanvasText(_.canvas, x - 4, y + l.CH / 2, anchor = 'e', font = font)) y = y + l.YS * 2 x = l.XM + l.XS * 1.5 y = l.YM for i in range(12): s.rows.append(GreatWall_RowStack(x, y, _, yoffset = l.CH / 4, max_cards = 26)) x = x + l.XS _.setRegion(s.rows, (l.XM + l.XS * 1.25, -999, _.width - l.XS * 1.25, 999999)) x = _.width / 2 - l.CW / 2 y = _.height - l.YS * 1.2 s.talon = InitialDealTalonStack(x, y, _) l.defaultStackGroups() def updateText(_): if _.preview > 1: return None for i in range(8): if t == 0: t = '' elif t == f: t = 'Full' _.foundation_texts[i].config(text = str(t)) def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48 * 4: raise AssertionError _.updateText() for i in range(15): _.s.talon.dealRow(flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError 0 def fillStack(_, stack): if stack in _.s.rows: if stack.cards and not (stack.cards[-1].face_up): _.flipMove(stack) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 12 == card2.rank: pass return (card1.rank - 1) % 12 == card2.rank class FourWinds(AbstractFlowerGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(7 * l.XS, 5 * l.YS + 3 * l.YM) TEXTS = ('North', 'East', 'South', 'West', 'NW', 'NE', 'SE', 'SW') x = l.XM * 3 y = l.YM xoffset = (0, 2.5, 5, 2.5) yoffset = (2, 0, 2, 4) for i in range(4): x0 = x + xoffset[i] * l.XS y0 = y + yoffset[i] * l.YS s.foundations.append(FourWinds_Foundation(x0, y0, _, i)) t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS + 5, anchor = 'center', font = font, text = TEXTS[i]) xoffset = (1.25, 3.75, 3.75, 1.25) yoffset = (0.75, 0.75, 3, 3) for i in range(4): x0 = x + xoffset[i] * l.XS y0 = y + yoffset[i] * l.YS s.rows.append(FourWinds_RowStack(x0, y0, _, yoffset = 10, max_cards = 3, max_accept = 1)) t = MfxCanvasText(_.canvas, x0 + l.CW / 2, y0 + l.YS + 5, anchor = 'center', font = font, text = TEXTS[i + 4]) _.setRegion(s.rows, (x + l.XS, y + l.YS * 0.65, x + l.XS * 4 + 5, y + l.YS * 3 + 5)) x = x + 2 * l.XS y = y + 2 * l.YS s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 2) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 48: raise AssertionError _.startDealSample() _.s.talon.dealCards() def fillStack(_, stack): if not (stack.cards) and stack is _.s.waste: if _.canDealCards(): _.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and (card1.rank + 1) % 12 == card2.rank: pass return (card1.rank - 1) % 12 == card2.rank def r(id, gameclass, name, game_type, decks, redeals): game_type = game_type | GI.GT_HANAFUDA | GI.GT_CONTRIB gi = GameInfo(id, gameclass, name, game_type, decks, redeals, ranks = range(12)) registerGame(gi) return gi r(348, FlowerClock, 'Flower Clock', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0) r(347, Gaji, 'Gaji', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0) r(345, Oonsoo, 'Oonsoo', GI.GT_HANAFUDA, 1, 0) r(349, Pagoda, 'Pagoda', GI.GT_HANAFUDA, 2, 0) r(346, MatsuKiri, 'MatsuKiri', GI.GT_HANAFUDA | GI.GT_OPEN, 1, 0) r(350, Samuri, 'Samuri', GI.GT_HANAFUDA, 1, 0) r(351, GreatWall, 'Great Wall', GI.GT_HANAFUDA, 4, 0) r(352, FourWinds, 'Four Winds', GI.GT_HANAFUDA, 1, 1) del r class TowerOfHanoy_Hint(CautiousDefaultHint): pass class TowerOfHanoy_RowStack(BasicRowStack): def acceptsCards(_, from_stack, cards): if not BasicRowStack.acceptsCards(_, from_stack, cards): return 0 if not (_.cards): return 1 return _.cards[-1].rank > cards[0].rank def getBottomImage(_): return _.game.app.images.getReserveBottom() class TowerOfHanoy(Game): RowStack_Class = TowerOfHanoy_RowStack Hint_Class = TowerOfHanoy_Hint def createGame(_): (l, s) = (Layout(_), _.s) h = max(2 * l.YS, l.YS + (len(_.cards) - 1) * l.YOFFSET + l.YM) _.setSize(l.XM + 5 * l.XS, l.YM + l.YS + h) for i in range(3): (x, y) = (l.XM + (i + 1) * l.XS, l.YM) s.rows.append(_.RowStack_Class(x, y, _, max_accept = 1, max_move = 1)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): _.startDealSample() for i in range(3): _.s.talon.dealRow() def isGameWon(_): for s in _.s.rows: pass return 0 def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank def getAutoStacks(_, event = None): return ((), (), _.sg.dropstacks) class HanoiPuzzle_RowStack(TowerOfHanoy_RowStack): def getBottomImage(_): if _.id == len(_.game.s.rows) - 1: return _.game.app.images.getSuitBottom() return _.game.app.images.getReserveBottom() class HanoiPuzzle4(TowerOfHanoy): RowStack_Class = HanoiPuzzle_RowStack def _shuffleHook(_, cards): return _._shuffleHookMoveToTop(cards, (lambda c: (1, -(c.id)))) def startGame(_): _.startDealSample() for i in range(len(_.cards)): _.s.talon.dealRow(rows = _.s.rows[:1]) def isGameWon(_): return len(_.s.rows[-1].cards) == len(_.cards) class HanoiPuzzle5(HanoiPuzzle4): pass class HanoiPuzzle6(HanoiPuzzle4): pass registerGame(GameInfo(124, TowerOfHanoy, 'Tower of Hanoy', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(9))) registerGame(GameInfo(207, HanoiPuzzle4, 'Hanoi Puzzle 4', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(4), rules_filename = 'hanoipuzzle.html')) registerGame(GameInfo(208, HanoiPuzzle5, 'Hanoi Puzzle 5', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(5), rules_filename = 'hanoipuzzle.html')) registerGame(GameInfo(209, HanoiPuzzle6, 'Hanoi Puzzle 6', GI.GT_PUZZLE_TYPE, 1, 0, suits = (2,), ranks = range(6), rules_filename = 'hanoipuzzle.html')) class HexADeck_FoundationStack(SS_FoundationStack): def __init__(_, x, y, game, suit, **cap): kwdefault(cap, max_move = 0, max_cards = 12) apply(SS_FoundationStack.__init__, (_, x, y, game, suit), cap) class HexATrump_Foundation(HexADeck_FoundationStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 for s in _.game.s.foundations[:3]: pass return 1 class HexADeck_OpenStack(OpenStack): def __init__(_, x, y, game, yoffset, **cap): kwdefault(cap, max_move = 999999) apply(OpenStack.__init__, (_, x, y, game), cap) _.CARD_YOFFSET = yoffset class Bits_RowStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if stackcards or cards[0].suit == 4: return 0 i = _.id / 4 for r in _.game.s.rows[i * 4:_.id]: pass return (_.game.s.foundations[i].cards[-1].rank + 1 >> _.id % 4) % 2 == (cards[0].rank + 1) % 2 class Bytes_RowStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 stackcards = _.cards if stackcards or cards[0].suit == 4: return 0 id = _.id - 16 i = id / 2 for r in _.game.s.rows[16 + i * 2:_.id]: pass return _.game.s.foundations[i].cards[-1].rank == cards[0].rank class HexAKlon_RowStack(AC_RowStack): def acceptsCards(_, from_stack, cards): if AC_RowStack.acceptsCards(_, from_stack, cards): return 1 stackcards = _.cards if not not stackcards and stackcards[-1].suit == 4: pass return cards[0].suit == 4 class BitsNBytes(Game): GAME_VERSION = 2 def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM * 4 + l.XS * 8, l.YM + l.YS * 4) y = l.YM for j in range(4): x = l.XM * 4 + l.XS * 7 for i in range(4): s.rows.append(Bits_RowStack(x, y, _, max_cards = 1, max_accept = 1, base_suit = j, max_move = 0)) x = x - l.XS y = y + l.YS y = l.YM for j in range(4): x = l.XM * 3 + l.XS * 3 for i in range(2): s.rows.append(Bytes_RowStack(x, y, _, max_cards = 1, max_accept = 1, base_suit = ANY_SUIT, max_move = 0)) x = x - l.XS y = y + l.YS x = l.XM * 2 + l.XS y = l.YM for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 1, max_move = 0, max_cards = 1)) y = y + l.YS _.setRegion(s.rows, (0, 0, 999999, 999999)) x = l.XM y = l.YM s.talon = WasteTalonStack(x, y, _, num_deal = 2, max_rounds = 2) l.createText(s.talon, 'ss') y = y + l.YS + l.YM * 2 s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') l.defaultStackGroups() def _shuffleHook(_, cards): (topcards, ranks) = ([ None] * 4, [ None] * 4) for c in cards[:]: pass cards = topcards + cards cards.reverse() return cards def startGame(_): if not __debug__ and len(_.s.talon.cards) == 68: raise AssertionError _.startDealSample() _.s.talon.dealRow(rows = _.s.foundations) _.s.talon.dealCards() def isGameWon(_): for s in _.s.rows: pass return 1 def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 class HexAKlon(Game): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 8, l.YM + l.YS * 5) x = l.XM y = l.YM s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = -1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') x = l.XM + l.XS * 3 for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, mod = 16, max_move = 16, max_cards = 16)) x = x + l.XS s.foundations.append(HexATrump_Foundation(x, y, _, 4, mod = 4, max_move = 0, max_cards = 4)) x = l.XM y = l.YM * 3 + l.YS for i in range(8): s.rows.append(HexAKlon_RowStack(x, y, _, max_cards = 99, max_accept = 99, base_suit = ANY_SUIT, base_rank = 15, max_move = 99)) x = x + l.XS _.setRegion(s.rows, (0, y - l.YM / 2, 999999, 999999)) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 68: raise AssertionError for i in range(len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i + 1:], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 registerGame(GameInfo(165, BitsNBytes, 'Bits n Bytes', GI.GT_HEXADECK, 1, 1, suits = range(4), ranks = range(16), trumps = range(4))) registerGame(GameInfo(166, HexAKlon, 'Hex A Klon', GI.GT_HEXADECK, 1, -1, suits = range(4), ranks = range(16), trumps = range(4))) class Memory_RowStack(OpenStack): def clickHandler(_, event): game = _.game if len(_.cards) != 1 or _.cards[-1].face_up: return 1 if game.other_stack is None: game.playSample('flip', priority = 5) _.flipMove() game.other_stack = _ elif __debug__: if not len(game.other_stack.cards) == 1 and game.other_stack.cards[-1].face_up: raise AssertionError (c1, c2) = (_.cards[-1], game.other_stack.cards[0]) _.flipMove() if _.game.cardsMatch(c1, c2): _._dropPairMove(1, game.other_stack) else: game.playSample('flip', priority = 5) game.score = game.score - 1 game.updateStatus(moves = game.moves.index + 1) game.updateText() game.canvas.update_idletasks() game.sleep(0.5) game.other_stack.flipMove() game.canvas.update_idletasks() game.sleep(0.2) _.flipMove() game.other_stack = None _.game.finishMove() return 1 def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game game.playSample('droppair', priority = 200) game.closed_cards = game.closed_cards - 2 game.score = game.score + 5 rightclickHandler = clickHandler doubleclickHandler = clickHandler def controlclickHandler(_, event): return 0 def shiftclickHandler(_, event): return 0 class Memory24(Game): Hint_Class = None COLUMNS = 6 ROWS = 4 WIN_SCORE = 40 PERFECT_SCORE = 60 def createGame(_): (l, s) = (Layout(_), _.s) _.other_stack = None _.closed_cards = -1 _.score = 0 (x, y) = (l.XM, _.ROWS * l.YS) if _.preview <= 1: _.texts.score = MfxCanvasText(_.canvas, x, y, anchor = 'sw', font = getFont('canvas_large')) x = _.texts.score.bbox()[2] + 16 w = max(2 * l.XS, x) _.setSize(l.XM + w + _.COLUMNS * l.XS, l.YM + _.ROWS * l.YS) for i in range(_.ROWS): for j in range(_.COLUMNS): (x, y) = (l.XM + w + j * l.XS, l.YM + i * l.YS) s.rows.append(Memory_RowStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 1)) (x, y) = (l.XM, l.YM) s.talon = InitialDealTalonStack(x, y, _) l.createText(s.talon, anchor = 'nn', text_format = '%D') s.internals.append(InvisibleStack(_)) l.defaultStackGroups() def startGame(_): n = _.COLUMNS * _.ROWS if not __debug__ and len(_.s.talon.cards) == n: raise AssertionError _.other_stack = None _.closed_cards = n _.score = 0 _.updateText() n = n - _.COLUMNS _.s.talon.dealRow(rows = _.s.rows[:n], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[n:], flip = 0) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError def isGameWon(_): if _.closed_cards == 0: pass return _.score >= _.WIN_SCORE def getAutoStacks(_, event = None): return ((), (), ()) def updateText(_): if _.preview > 1 or not (_.texts.score): return None t = '' if _.closed_cards: t = 'Points: %d' % _.score elif _.score >= _.WIN_SCORE: t = 'WON\n\n' t = t + 'Total: %d' % _.score _.texts.score.config(text = t) def getGameScore(_): return _.score def getWinStatus(_): (won, status, updated) = Game.getWinStatus(_) if status == 2 and _.score < _.PERFECT_SCORE: return (won, 1, _.U_WON) return (won, status, updated) def cardsMatch(_, card1, card2): if card1.suit == card2.suit: pass return card1.rank == card2.rank def canSaveGame(_): return 0 def canUndo(_): return 0 def _restoreGameHook(_, game): if game.loadinfo.other_stack_id >= 0: _.other_stack = _.allstacks[game.loadinfo.other_stack_id] else: _.other_stack = None _.closed_cards = game.loadinfo.closed_cards _.score = game.loadinfo.score def _loadGameHook(_, p): _.loadinfo.addattr(other_stack_id = p.load()) _.loadinfo.addattr(closed_cards = p.load()) _.loadinfo.addattr(score = p.load()) def _saveGameHook(_, p): if _.other_stack: p.dump(_.other_stack.id) else: p.dump(-1) p.dump(_.closed_cards) p.dump(_.score) class Memory30(Memory24): COLUMNS = 6 ROWS = 5 WIN_SCORE = 45 PERFECT_SCORE = 75 class Memory40(Memory24): COLUMNS = 8 ROWS = 5 WIN_SCORE = 50 PERFECT_SCORE = 100 class Concentration_RowStack(Memory_RowStack): def _dropPairMove(_, n, other_stack, frames = -1, shadow = -1): game = _.game game.playSample('droppair', priority = 200) game.closed_cards = game.closed_cards - 2 game.score = game.score + 5 old_state = game.enterState(game.S_FILL) f = game.s.talon game.moveMove(n, _, f, frames = frames, shadow = shadow) game.moveMove(n, other_stack, f, frames = frames, shadow = shadow) game.leaveState(old_state) class Concentration(Memory24): COLUMNS = 13 ROWS = 4 WIN_SCORE = 50 PERFECT_SCORE = 130 def createGame(_): (l, s) = (Layout(_, XM = 4), _.s) _.other_stack = None _.closed_cards = -1 _.score = 0 _.setSize(l.XM + _.COLUMNS * l.XS, l.YM + (_.ROWS + 1) * l.YS) for i in range(_.ROWS): for j in range(_.COLUMNS): (x, y) = (l.XM + j * l.XS, l.YM + i * l.YS) s.rows.append(Concentration_RowStack(x, y, _, max_move = 0, max_accept = 0, max_cards = 1)) (x, y) = (l.XM + _.COLUMNS * l.XS / 2, _.height - l.YS) s.talon = InitialDealTalonStack(x, y, _) l.createText(s.talon, dx = -10, anchor = 'sw', text_format = '%D') (x, y) = (l.XM, _.height - l.YM) l.defaultStackGroups() def cardsMatch(_, card1, card2): return card1.rank == card2.rank registerGame(GameInfo(176, Memory24, 'Memory 24', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2), ranks = (0, 8, 9, 10, 11, 12))) registerGame(GameInfo(219, Memory30, 'Memory 30', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2, 3), ranks = (0, 9, 10, 11, 12))) registerGame(GameInfo(177, Memory40, 'Memory 40', GI.GT_MEMORY | GI.GT_SCORE, 2, 0, suits = (0, 2), ranks = (0, 4, 5, 6, 7, 8, 9, 10, 11, 12))) registerGame(GameInfo(178, Concentration, 'Concentration', GI.GT_MEMORY | GI.GT_SCORE, 1, 0)) class Pegged_Hint(AbstractHint): def computeHints(_): game = _.game stacks = filter((lambda r: not (r.cards)), game.s.rows) for t in stacks: for dx, dy in game.STEPS: r = game.map.get((t.pos[0] + dx, t.pos[1] + dy)) score = 10000 + game.app.miscrandom.randint(0, 9999) _.addHint(score, 1, r, t) class Pegged_RowStack(ReserveStack): def acceptsCards(_, from_stack, cards): if not ReserveStack.acceptsCards(_, from_stack, cards): return 0 return _._getMiddleStack(from_stack) is not None def canDropCards(_, stacks): return (None, 0) def moveMove(_, ncards, to_stack, frames = -1, shadow = -1): other_stack = to_stack._getMiddleStack(_) old_state = _.game.enterState(_.game.S_FILL) f = _.game.s.foundations[0] _.game.moveMove(ncards, _, to_stack, frames = 0) _.game.playSample('drop', priority = 200) _.game.moveMove(ncards, other_stack, f, frames = -1, shadow = shadow) _.game.leaveState(old_state) _.fillStack() other_stack.fillStack() def _getMiddleStack(_, from_stack): (dx, dy) = (from_stack.pos[0] - _.pos[0], from_stack.pos[1] - _.pos[1]) if not _.game.STEP_MAP.get((dx, dy)): return None s = _.game.map.get((_.pos[0] + dx / 2, _.pos[1] + dy / 2)) if not s or not (s.cards): return None return s def copyModel(_, clone): ReserveStack.copyModel(_, clone) clone.pos = _.pos class Pegged(Game): Hint_Class = Pegged_Hint STEPS = ((-4, 0), (4, 0), (0, -4), (0, 4)) ROWS = (3, 5, 7, 7, 7, 5, 3) EMPTY_STACK_ID = -1 def createGame(_): (l, s) = (Layout(_), _.s) n = m = max(_.ROWS) if _.ROWS[0] == m or _.ROWS[-1] == m: n = n + 1 _.setSize(l.XM + n * l.XS, l.YM + len(_.ROWS) * l.YS) _.map = { } for i in range(len(_.ROWS)): r = _.ROWS[i] for j in range(r): d = (m - r) + 2 * j (x, y) = (l.XM + d * l.XS / 2, l.YM + i * l.YS) stack = Pegged_RowStack(x, y, _) stack.pos = (d, 2 * i) s.rows.append(stack) _.map[stack.pos] = stack (x, y) = (_.width - l.XS, l.YM) s.foundations.append(AbstractFoundationStack(x, y, _, ANY_SUIT, max_move = 0, max_accept = 0)) l.createText(s.foundations[0], 'ss') y = _.height - l.YS s.talon = InitialDealTalonStack(x, y, _) s.internals.append(InvisibleStack(_)) _.STEP_MAP = { } for step in _.STEPS: _.STEP_MAP[step] = 1 l.defaultStackGroups() def startGame(_): n = (len(_.cards) - len(_.s.rows)) + 1 if n > 0: _.moveMove(n, _.s.talon, _.s.internals[0], frames = 0) _.startDealSample() rows = list(_.s.rows[:]) rows.remove(rows[_.EMPTY_STACK_ID]) _.s.talon.dealRow(rows = rows, frames = 4) if not __debug__ and len(_.s.talon.cards) == 0: raise AssertionError def isGameWon(_): c = 0 for s in _.s.foundations: c = c + len(s.cards) return c + 1 == len(_.cards) def getAutoStacks(_, event = None): return ((), (), ()) def getWinStatus(_): (won, status, updated) = Game.getWinStatus(_) if status == 2: stacks = filter((lambda r: r.cards), game.s.rows) if not __debug__ and len(stacks) == 1: raise AssertionError if stacks[0].id != _.EMPTY_STACK_ID: return (won, 1, _.U_WON) return (won, status, updated) def getHighlightPilesStacks(_): rows = [] for r in _.s.rows: (rx, ry) = r.pos for dx, dy in _.STEPS: s = _.map.get((rx + dx, ry + dy)) return ((rows, 1),) class PeggedCross1(Pegged): ROWS = (3, 3, 7, 7, 7, 3, 3) class PeggedCross2(Pegged): ROWS = (3, 3, 3, 9, 9, 9, 3, 3, 3) class Pegged6x6(Pegged): EMPTY_STACK_ID = 14 ROWS = (6, 6, 6, 6, 6, 6) class Pegged7x7(Pegged): ROWS = (7, 7, 7, 7, 7, 7, 7) class PeggedTriangle1(Pegged): STEPS = ((-2, -4), (-2, 4), (0, -4), (0, 4), (2, -4), (2, 4)) ROWS = (1, 2, 3, 4, 5) EMPTY_STACK_ID = 4 class PeggedTriangle2(PeggedTriangle1): ROWS = (1, 2, 3, 4, 5, 6) def r(id, gameclass, name): si_ncards = 0 for n in gameclass.ROWS: si_ncards = si_ncards + n gi = GameInfo(id, gameclass, name, GI.GT_PUZZLE_TYPE, 1, 0, si = { 'ncards': si_ncards }, rules_filename = 'pegged.html') registerGame(gi) return gi r(180, Pegged, 'Pegged') r(181, PeggedCross1, 'Pegged Cross 1') r(182, PeggedCross2, 'Pegged Cross 2') r(183, Pegged6x6, 'Pegged 6x6') r(184, Pegged7x7, 'Pegged 7x7') r(210, PeggedTriangle1, 'Pegged Triangle 1') r(211, PeggedTriangle2, 'Pegged Triangle 2') del r class Wicked_Talon(Cruel_Talon): pass class ImperialTrump_Foundation(SS_FoundationStack): def acceptsCards(_, from_stack, cards): if not SS_FoundationStack.acceptsCards(_, from_stack, cards): return 0 return cards[-1].rank < len(_.game.s.foundations[4].cards) class Ponytail_Foundation(Braid_Foundation): pass class Tarock_OpenStack(OpenStack): def __init__(_, x, y, game, yoffset = -1, **cap): kwdefault(cap, max_move = 999999, max_accept = 999999) apply(OpenStack.__init__, (_, x, y, game), cap) if yoffset < 0: yoffset = game.app.images.CARD_YOFFSET _.CARD_YOFFSET = yoffset class Tarock_AC_RowStack(Tarock_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): return 1 if cards[0].rank != _.cards[-1].rank - 1: return 0 elif cards[0].color == 2 or _.cards[-1].color == 2: return 1 else: return cards[0].color != _.cards[-1].color class Skiz_RowStack(RK_RowStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): if cards[0].suit == len(_.game.gameinfo.suits): return cards[0].rank == len(_.game.gameinfo.trumps) - 1 else: return cards[0].rank == len(_.game.gameinfo.ranks) - 1 if _.cards[-1].suit == cards[0].suit: pass return _.cards[-1].rank - 1 == cards[0].rank class Pagat_RowStack(RK_RowStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): return 1 if _.cards[-1].suit == cards[0].suit: pass return _.cards[-1].rank - 1 == cards[0].rank class TrumpWild_RowStack(Tarock_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): if cards[0].suit == len(_.game.gameinfo.suits): return cards[0].rank == len(_.game.gameinfo.trumps) - 1 else: return cards[0].rank == len(_.game.gameinfo.ranks) - 1 if cards[0].rank != _.cards[-1].rank - 1: return 0 elif cards[0].color == 2 or _.cards[-1].color == 2: return 1 else: return cards[0].color != _.cards[-1].color class TrumpOnly_RowStack(Tarock_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): return cards[0].suit == len(_.game.gameinfo.suits) if cards[0].color == 2: pass return cards[0].rank == _.cards[-1].rank - 1 def getBottomImage(_): return _.game.app.images.getReserveBottom() class Excuse_RowStack(Tarock_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): return 0 return cards[0].rank == _.cards[-1].rank - 1 class WheelOfFortune_RowStack(Tarock_OpenStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if not (_.cards): return 1 if cards[0].suit == _.cards[-1].suit: pass return cards[0].rank == _.cards[-1].rank - 1 def getBottomImage(_): return _.game.app.images.getReserveBottom() class Ponytail_PonytailStack(Braid_BraidStack): pass class Ponytail_RowStack(Braid_RowStack): pass class Ponytail_ReserveStack(Braid_ReserveStack): pass class Cavalier_RowStack(Tarock_AC_RowStack): def acceptsCards(_, from_stack, cards): if not Tarock_AC_RowStack.acceptsCards(_, from_stack, cards): return 0 if not _.cards: pass return len(cards) == 1 def canMoveCards(_, cards): for i in range(len(cards) - 1): if not (cards[i].suit == 4): if cards[i].color == cards[i + 1].color: return 0 if cards[i].rank - 1 != cards[i + 1].rank: return 0 return 1 class Nasty_RowStack(SS_RowStack): def acceptsCards(_, from_stack, cards): if not _.basicAcceptsCards(from_stack, cards): return 0 if _.cards: if cards[0].rank == _.cards[-1].rank - 1: pass return cards[0].suit == _.cards[-1].suit return cards[0].rank == 13 + 8 * (cards[0].suit == 4) class Tarock_GameMethods: SUITS = ('Wand', 'Sword', 'Cup', 'Coin', 'Trump') RANKS = ('Ace', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'Page', 'Valet', 'Queen', 'King') def getCardFaceImage(_, deck, suit, rank): return _.app.images.getFace(deck, suit, rank) class AbstractTarockGame(Tarock_GameMethods, Game): pass class WheelOfFortune(AbstractTarockGame): Hint_Class = CautiousDefaultHint def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 11.5, l.YM + l.YS * 5.5) xoffset = (1, 2, 3, 3.9, 3, 2, 1, 0, -1, -2, -3, -3.9, -3, -2, -1, 0, -2, -1, 0, 1, 2) yoffset = (0.2, 0.5, 1.1, 2.2, 3.3, 3.9, 4.2, 4.4, 4.2, 3.9, 3.3, 2.2, 1.1, 0.5, 0.2, 0, 1.8, 2.1, 2.2, 2.4, 2.6) x = l.XM + l.XS * 4 y = l.YM for i in range(21): x0 = x + xoffset[i] * l.XS y0 = y + yoffset[i] * l.YS s.rows.append(WheelOfFortune_RowStack(x0, y0, _, yoffset = l.CH / 4, max_cards = 2, max_move = 1, max_accept = 1)) _.setRegion(s.rows, (-999, -999, l.XS * 9, 999999)) x = _.width - l.XS * 2 y = l.YM s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14)) y = y + l.YS s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14)) x = x - l.XS s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14)) x = x + l.XS * 0.5 y = y + l.YS s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) x = _.width - l.XS y = _.height - l.YS * 1.5 s.talon = WasteTalonStack(x, y, _, num_deal = 2, max_rounds = 1) l.createText(s.talon, 'nn') x = x - l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'nn') l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[-5:]) _.s.talon.dealRow(rows = _.s.rows[4:-5]) _.s.talon.dealRow(rows = _.s.rows[:4]) _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 class ImperialTrumps(AbstractTarockGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) _.setSize(l.XM + l.XS * 8, l.YM + l.YS * 5) x = l.XM + l.XS * 3 y = l.YM for i in range(4): s.foundations.append(ImperialTrump_Foundation(x, y, _, i, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) x = l.XM s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = -1) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') x = l.XM y = l.YM + int(round(l.YS * 1.25)) for i in range(8): s.rows.append(TrumpWild_RowStack(x, y, _)) x = x + l.XS _.setRegion(s.rows, (-999, y, 999999, 999999)) l.defaultStackGroups() def startGame(_, reverse = 1): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for i in range(1, len(_.s.rows)): _.s.talon.dealRow(rows = _.s.rows[i:], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow(reverse = reverse) _.s.talon.dealCards() def shallHighlightMatch(_, stack1, card1, stack2, card2): return 0 class Pagat(AbstractTarockGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) h = max(3 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h) x = l.XM + l.XS * 3.5 y = l.YM s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14)) x = l.XM for i in range(3): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS x = x + l.XS * 6 for i in range(3): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS x = l.XM y = l.YM + l.YS * 1.1 for i in range(12): s.rows.append(Pagat_RowStack(x, y, _)) x = x + l.XS _.setRegion(s.rows, (-999, int(y), 999999, 999999)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for i in range(6): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[3:9]) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Skiz(AbstractTarockGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) h = max(3 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + 12 * l.XS, l.YM + l.YS + h) x = l.XM + l.XS * 3.5 y = l.YM s.foundations.append(SS_FoundationStack(x, y, _, 0, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 1, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 2, max_cards = 14)) x = x + l.XS s.foundations.append(SS_FoundationStack(x, y, _, 3, max_cards = 14)) x = l.XM for i in range(3): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS x = x + l.XS * 6 for i in range(3): s.reserves.append(ReserveStack(x, y, _)) x = x + l.XS x = l.XM y = l.YM + l.YS * 1.1 for i in range(12): s.rows.append(Skiz_RowStack(x, y, _)) x = x + l.XS _.setRegion(s.rows, (-999, int(y), 999999, 999999)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for i in range(6): _.s.talon.dealRow(frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[3:9]) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class FifteenPlus(AbstractTarockGame): def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) h = max(5 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h) x = _.width - l.XS y = l.YM s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) y = y + l.YS for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, max_cards = 14)) y = y + l.YS x = l.XM y = l.YM for j in range(2): for i in range(8): s.rows.append(Tarock_AC_RowStack(x, y, _, max_move = 1, max_accept = 1)) x = x + l.XS x = l.XM y = y + l.YS * 3 _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def startGame(_): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for i in range(2): _.s.talon.dealRow(flip = 0, frames = 0) for i in range(2): _.s.talon.dealRow(rows = _.s.rows[:15], flip = 0, frames = 0) _.startDealSample() _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.suit == card2.suit and card1.rank + 1 == card2.rank: pass return card2.rank + 1 == card1.rank class Excuse(AbstractTarockGame): GAME_VERSION = 2 def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) h = max(5 * l.YS, 20 * l.YOFFSET) _.setSize(l.XM + 9 * l.XS, l.YM + l.YS + h) x = _.width - l.XS y = l.YM s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) y = y + l.YS for i in range(4): s.foundations.append(SS_FoundationStack(x, y, _, i, max_cards = 14)) y = y + l.YS x = l.XM y = l.YM for j in range(2): for i in range(8): s.rows.append(Excuse_RowStack(x, y, _, max_move = 1, max_accept = 1, base_rank = NO_RANK)) x = x + l.XS x = l.XM y = y + l.YS * 3 _.setRegion(s.rows, (-999, -999, l.XM + l.XS * 8, 999999)) s.talon = InitialDealTalonStack(l.XM, _.height - l.YS, _) l.defaultStackGroups() def _shuffleHook(_, cards): def isKing(c): if c.suit < 4 and c.rank == 13 and c.suit == 4: pass return c.rank == 21 (i, n) = (0, len(_.s.rows)) kings = [] for c in cards: i = i + 1 for i in kings: j = i % n while j < i: j = j + n continue None if isKing(c) else cards if not isKing(cards[j]) else kings cards.reverse() return cards def startGame(_): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for i in range(3): _.s.talon.dealRow(frames = 0) _.s.talon.dealRow(rows = _.s.rows[:15], frames = 0) _.startDealSample() _.s.talon.dealRow(rows = _.s.rows[:15]) def shallHighlightMatch(_, stack1, card1, stack2, card2): if not card1.rank + 1 == card2.rank: pass return card1.rank - 1 == card2.rank class Grasshopper(AbstractTarockGame): GAME_VERSION = 2 def createGame(_): (l, s) = (Layout(_), _.s) font = getFont('canvas_card', cardw = l.CW) decks = _.gameinfo.decks _.setSize(2 * l.XM + (2 + 5 * decks) * l.XS, 3 * l.YM + 5 * l.YS) yoffset = min(l.YOFFSET, max(10, l.YOFFSET / 2)) x = l.XM y = l.YM s.talon = WasteTalonStack(x, y, _, num_deal = 1, max_rounds = 2) l.createText(s.talon, 'ss') x = x + l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') x = x + l.XM + l.XS for j in range(4): for i in range(decks): s.foundations.append(SS_FoundationStack(x, y, _, j, max_cards = 14)) x = x + l.XS for i in range(decks): s.foundations.append(SS_FoundationStack(x, y, _, 4, max_cards = 22)) x = x + l.XS x = l.XM y = l.YM * 3 + l.YS s.reserves.append(OpenStack(x, y, _)) s.reserves[0].CARD_YOFFSET = (l.YOFFSET, yoffset)[decks == 2] x = x + l.XM + l.XS for i in range(decks): s.rows.append(TrumpOnly_RowStack(x, y, _, yoffset = yoffset)) x = x + l.XS for i in range(4 * decks + 1): s.rows.append(Tarock_AC_RowStack(x, y, _)) x = x + l.XS _.setRegion(s.rows, (-999, y - l.YS, 999999, 999999)) l.defaultStackGroups() def startGame(_): decks = _.gameinfo.decks if not __debug__ and len(_.s.talon.cards) == 78 * decks: raise AssertionError _.startDealSample() for i in range(14 * decks): _.s.talon.dealRow(rows = _.s.reserves, flip = 0, frames = 4) _.s.reserves[0].flipMove() _.s.talon.dealRow(rows = _.s.rows[decks:]) _.s.talon.dealCards() def fillStack(_, stack): r = _.s.reserves[0] if not (stack.cards) and stack in _.s.rows: if r.cards and stack.acceptsCards(r, r.cards[-1:]): r.moveMove(1, stack) if r.canFlipCard(): r.flipMove() def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank: pass return card1.color != card2.color class DoubleGrasshopper(Grasshopper): pass class Ponytail(Tarock_GameMethods, Braid): def createGame(_): (l, s) = (Layout(_), _.s) h = max(5 * l.YS + 30, l.YS + (_.BRAID_CARDS - 1) * l.YOFFSET) _.setSize(10 * l.XS + l.XM, l.YM + h) _.base_card = None s.addattr(braid = None) (x, y) = (l.XM, l.YM) for i in range(2): s.rows.append(Ponytail_RowStack(x + 0.5 * l.XS, y, _)) s.rows.append(Ponytail_RowStack(x + 4.5 * l.XS, y, _)) s.rows.append(Ponytail_RowStack(x + 5.5 * l.XS, y, _)) s.rows.append(Ponytail_RowStack(x + 6.5 * l.XS, y, _)) y = y + 4 * l.YS y = l.YM + l.YS for i in range(2): s.rows.append(Ponytail_ReserveStack(x, y, _)) s.rows.append(Ponytail_ReserveStack(x + l.XS, y, _)) s.rows.append(Ponytail_ReserveStack(x, y + l.YS, _)) s.rows.append(Ponytail_ReserveStack(x + l.XS, y + l.YS, _)) s.rows.append(Ponytail_ReserveStack(x, y + 2 * l.YS, _)) s.rows.append(Ponytail_ReserveStack(x + l.XS, y + 2 * l.YS, _)) x = x + 4 * l.XS x = l.XM + 5 * l.XS / 2 y = l.YM s.braid = Ponytail_PonytailStack(x, y, _, sine = 1) x = l.XM + 7 * l.XS y = l.YM + 2 * l.YS s.talon = WasteTalonStack(x, y, _, max_rounds = 3) l.createText(s.talon, 'ss') s.talon.texts.rounds = MfxCanvasText(_.canvas, x + l.CW / 2, y - l.YM, anchor = 's') x = x - l.XS s.waste = WasteStack(x, y, _) l.createText(s.waste, 'ss') x = l.XM + 8 * l.XS y = l.YM for i in range(4): s.foundations.append(Ponytail_Foundation(x, y, _, i, mod = 14, max_cards = 14)) s.foundations.append(Ponytail_Foundation(x + l.XS, y, _, i, mod = 14, max_cards = 14)) y = y + l.YS s.foundations.append(Ponytail_Foundation(x, y, _, 4, mod = 22, max_cards = 22)) s.foundations.append(Ponytail_Foundation(x + l.XS, y, _, 4, mod = 22, max_cards = 22)) _.texts.info = MfxCanvasText(_.canvas, x + l.CW + l.XM / 2, y + l.YS, anchor = 'n', font = getFont('canvas_card', cardw = l.CW)) _.sg.openstacks = s.foundations + s.rows _.sg.talonstacks = [ s.talon] + [ s.waste] _.sg.dropstacks = [ s.braid] + s.rows + [ s.waste] class Cavalier(AbstractTarockGame): Layout_Method = Layout.bakersDozenLayout Talon_Class = InitialDealTalonStack Foundation_Class = SS_FoundationStack RowStack_Class = Cavalier_RowStack def createGame(_, **layout): (l, s) = (Layout(_), _.s) kwdefault(layout, rows = 18, playcards = 19) apply(_.Layout_Method, (l,), layout) _.setSize(l.size[0], l.size[1]) for r in l.s.foundations: n = 14 + 8 * (r.suit == 4) s.foundations.append(_.Foundation_Class(r.x, r.y, _, r.suit, mod = n, max_cards = n)) for r in l.s.rows: s.rows.append(_.RowStack_Class(r.x, r.y, _)) s.talon = _.Talon_Class(l.s.talon.x, l.s.talon.y, _) l.defaultAll() def startGame(_, flip = (0, 1, 0), foundations = 0): if not __debug__ and len(_.s.talon.cards) == 78: raise AssertionError for f in flip: _.s.talon.dealRow(flip = f, frames = 0) _.startDealSample() _.s.talon.dealRow() def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank: if not card1.suit == 4 or card2.suit == 4: pass return card1.color != card2.color class FiveAces(Cavalier): def _shuffleHook(_, cards): return _._shuffleHookMoveToBottom(cards, (lambda c: (c.rank == 0, c.suit))) def startGame(_): Cavalier.startGame(_, foundations = 1) class Wicked(FiveAces): Talon_Class = StackWrapper(Wicked_Talon, max_rounds = -1) RowStack_Class = StackWrapper(SS_RowStack, max_move = 1, max_accept = 1, base_rank = NO_RANK) Hint_Class = CautiousDefaultHint def startGame(_): Cavalier.startGame(_, flip = (1, 1, 1), foundations = 1) def shallHighlightMatch(_, stack1, card1, stack2, card2): if card1.rank + 1 == card2.rank or card1.rank - 1 == card2.rank: pass return card1.suit == card2.suit class Nasty(Wicked): RowStack_Class = StackWrapper(Nasty_RowStack, max_move = 1, max_accept = 1, base_rank = ANY_RANK) def r(id, gameclass, name, game_type, decks, redeals): game_type = game_type | GI.GT_TAROCK | GI.GT_CONTRIB | GI.GT_ORIGINAL gi = GameInfo(id, gameclass, name, game_type, decks, redeals, ranks = range(14), trumps = range(22)) registerGame(gi) return gi r(157, WheelOfFortune, 'Wheel of Fortune', GI.GT_TAROCK, 1, 0) r(158, ImperialTrumps, 'Imperial Trumps', GI.GT_TAROCK, 1, -1) r(159, Pagat, 'Pagat', GI.GT_TAROCK | GI.GT_OPEN, 1, 0) r(160, Skiz, 'Skiz', GI.GT_TAROCK | GI.GT_OPEN, 1, 0) r(161, FifteenPlus, 'Fifteen plus', GI.GT_TAROCK, 1, 0) r(162, Excuse, 'Excuse', GI.GT_TAROCK | GI.GT_OPEN, 1, 0) r(163, Grasshopper, 'Grasshopper', GI.GT_TAROCK, 1, 1) r(164, DoubleGrasshopper, 'Double Grasshopper', GI.GT_TAROCK, 2, 1) r(179, Ponytail, 'Ponytail', GI.GT_TAROCK, 2, 2) r(202, Cavalier, 'Cavalier', GI.GT_TAROCK, 1, 0) r(203, FiveAces, 'Five Aces', GI.GT_TAROCK, 1, 0) r(204, Wicked, 'Wicked', GI.GT_TAROCK | GI.GT_OPEN, 1, -1) r(205, Nasty, 'Nasty', GI.GT_TAROCK | GI.GT_OPEN, 1, -1) del r class Options: def __init__(_): _.version_tuple = VERSION_TUPLE _.saved = 0 _.player = 'unknown' _.confirm = 1 _.update_player_stats = 1 _.autofaceup = 1 _.autodrop = 0 _.autodeal = 1 _.quickplay = 1 _.undo = 1 _.bookmarks = 1 _.hint = 1 _.highlight_piles = 1 _.highlight_cards = 1 _.highlight_samerank = 1 _.tablecolor = '#008200' _.animations = 2 _.shadow = 0 _.shade = 1 _.hint_sleep = 1.5 _.demo_sleep = 1.5 _.demo_logo = 1 _.demo_score = 0 _.toolbar = 1 _.toolbar_size = 0 _.statusbar = 1 _.sound = 1 _.sound_mode = 1 _.sound_sample_volume = 128 _.sound_music_volume = 128 _.recent_gameid = [] _.last_gameid = 0 _.last_player = None _.last_save_dir = None _.game_holded = 0 _.wm_maximized = 0 _.setDefaults() _.setConstants() def setDefaults(_, top = None): (sw, sh, sd) = (0, 0, 8) if top: (sw, sh, sd) = (top.winfo_screenwidth(), top.winfo_screenheight(), top.winfo_screendepth()) if sd > 8: _.tabletile_name = 'Fade_Green.ppm' else: _.tabletile_name = None c = 'Standard' if sw < sw: pass elif not sw < 800: if sh < sh: pass elif sh < 600: c = '2000' _.cardset = { 0: (c, ''), CSI.TYPE_FRENCH: (c, ''), CSI.TYPE_HANAFUDA: ('Kintengu', ''), CSI.TYPE_TAROCK: ('Vienna 2K', ''), CSI.TYPE_HEXADECK: ('Hex A Deck', ''), CSI.TYPE_MUGHAL_GANJIFA: ('Dashavatara Ganjifa', ''), CSI.TYPE_NAVAGRAHA_GANJIFA: ('Dashavatara Ganjifa', ''), CSI.TYPE_DASHAVATARA_GANJIFA: ('Dashavatara Ganjifa', '') } def setConstants(_): _.win_animation = 1 _.toolbar_relief = 0 _.dragcursor = 1 _.magnetic_mouse = 0 _.magnetic_mouse_time = 2.0 _.raise_card_sleep = 1.0 _.highlight_piles_sleep = 1.5 _.highlight_piles_colors = (None, '#ffc000') _.highlight_cards_sleep = 1.5 _.highlight_cards_colors = (None, '#ffc000', None, '#0000ff') _.highlight_samerank_sleep = 1.5 _.highlight_samerank_colors = (None, '#ffc000', None, '#0000ff') _.hintarrow_color = '#303030' if PACKAGE == 'PyJongg': _.highlight_samerank = 0 _.shadow = 0 _.shade = 0 def copy(_): opt = Options() merge_dict(opt.__dict__, _.__dict__) opt.setConstants() return opt class Statistics: def __init__(_): _.version_tuple = VERSION_TUPLE _.saved = 0 _.stats = { } _.demo_stats = { } _.prev_games = { } _.all_prev_games = { } _.session_games = { } _.total_balance = { } _.session_balance = { } _.gameid_balance = 0 def new(_): return Statistics() def resetStats(_, player, gameid): _._Statistics__resetPrevGames(player, _.prev_games, gameid) _._Statistics__resetPrevGames(player, _.session_games, gameid) if not _.stats.has_key(player): return None if gameid == 0: del _.stats[player] else: (w0, l0) = _.getStats(player, 0) (w1, l1) = _.getStats(player, gameid) _.stats[player][0] = (w0 - w1, l0 - l1) _.stats[player][gameid] = (0, 0) def __resetPrevGames(_, player, games, gameid): if not games.has_key(player): return None if gameid == 0: del games[player] else: games[player] = filter((lambda a, b = gameid: a[0] != b), games[player]) def getStats(_, player, gameid): d = _.stats.get(player) if d is None: d = _.stats[player] = { } return d.get(gameid, (0, 0)) def updateStats(_, player, game, status): (won, lost) = (status > 0, status == 0) (w, l) = _.getStats(player, 0) _.stats[player][0] = (w + won, l + lost) (w, l) = _.getStats(player, game.id) _.stats[player][game.id] = (w + won, l + lost) _.updateLog(player, game, status) def updateLog(_, player, game, status): log = (game.id, game.getGameNumber(format = 0), status, game.gstats.start_time, game.gstats.total_elapsed_time, VERSION_TUPLE, game.getGameScore(), game.getGameScoreCasino(), game.GAME_VERSION) if player is not None and status >= 0: if not _.prev_games.has_key(player): _.prev_games[player] = [] _.prev_games[player].append(log) if not _.all_prev_games.has_key(player): _.all_prev_games[player] = [] _.all_prev_games[player].append(log) if not _.session_games.has_key(player): _.session_games[player] = [] _.session_games[player].append(log) class Comments: def __init__(_): _.version_tuple = VERSION_TUPLE _.saved = 0 _.comments = { } def new(_): return Comments() def setGameComment(_, gameid, text): player = None key = (1, gameid, player) _.comments[key] = str(text) def getGameComment(_, gameid): player = None key = (1, gameid, player) return _.comments.get(key, '') class Application: def __init__(_): _.starttimer = Timer('Application.__init__') _.gdb = GAME_DB _.opt = Options() _.startup_opt = _.opt.copy() _.stats = Statistics() _.comments = Comments() _.splashscreen = 1 _.debug = 0 _.top = None _.top_cursor = None _.menubar = None _.toolbar = None _.canvas = None _.statusbar = None _.game = None _.dataloader = None _.audio = None _.images = None _.subsampled_images = None _.gimages = Struct(border = [], demo = [], logos = [], redeal = [], shade = [], stats = []) _.progress_bg = None _.progress_images = [] _.cardset_manager = CardsetManager() _.cardset = None _.tabletile_manager = TileManager() _.tabletile_index = 0 _.sample_manager = SampleManager() _.music_manager = MusicManager() _.music_playlist = [] _.intro = Struct(progress = None) home = os.path.normpath(gethomedir()) config = os.path.normpath(getprefdir(PACKAGE, home)) _.dn = Struct(home = home, config = config, plugins = os.path.join(config, 'plugins'), savegames = os.path.join(config, 'savegames'), maint = os.path.join(config, 'maint')) for k, v in _.dn.__dict__.items(): v = os.path.normpath(v) _.dn.__dict__[k] = v _.fn = Struct(opt = os.path.join(_.dn.config, 'options.dat'), stats = os.path.join(_.dn.config, 'statistics.dat'), holdgame = os.path.join(_.dn.config, 'holdgame.dat'), comments = os.path.join(_.dn.config, 'comments.dat')) for k, v in _.dn.__dict__.items(): v = os.path.normpath(v) _.fn.__dict__[k] = v _.gamerandom = LCRandom64() _.miscrandom = LCRandom64() player = getusername() player = player[:30] _.opt.player = player _.nextgame = Struct(id = 0, random = None, loadedgame = None, startdemo = 0, cardset = None, holdgame = 0, bookmark = None) _.commandline = Struct(loadgame = None) _.demo_counter = 0 def mainloop(_): _.startup_opt = _.opt.copy() try: _.loadStatistics() except: pass try: _.loadComments() except: pass if _.getGameClass(_.opt.last_gameid): _.nextgame.id = _.opt.last_gameid id = _.gdb.getGamesIdSortedByName()[0] tmpgame = _.constructGame(id) if _.opt.game_holded > 0 and not (_.nextgame.loadedgame): game = None try: game = tmpgame._loadGame(_.fn.holdgame, _) except: game = None if game: if game.id == _.opt.game_holded and game.gstats.holded: game.gstats.loaded = game.gstats.loaded - 1 game.gstats.holded = 0 _.nextgame.loadedgame = game else: game.destruct() destruct(game) game = None if _.commandline.loadgame and not (_.nextgame.loadedgame): try: _.nextgame.loadedgame = tmpgame._loadGame(_.commandline.loadgame, _) _.nextgame.loadedgame.gstats.holded = 0 except: _.nextgame.loadedgame = None _.opt.game_holded = 0 tmpgame.destruct() destruct(tmpgame) tmpgame = None _.menubar = PysolMenubar(_, _.top) _.statusbar = PysolStatusbar(_.top) _.statusbar.show(_.opt.statusbar) dir = _.getToolbarImagesDir(_.opt.toolbar_size) _.toolbar = PysolToolbar(_.top, dir = dir, size = _.opt.toolbar_size) _.toolbar.setRelief(_.opt.toolbar_relief) _.toolbar.show(_.opt.toolbar) if _.intro.progress: _.intro.progress.update(step = 1) _.canvas = MfxCanvas(_.top, bg = _.opt.tablecolor, highlightthickness = 0) _.canvas.pack(fill = 'both', expand = 1) tile = _.tabletile_manager.get(_.tabletile_index) _.canvas.setTile(tile.filename) _.canvas.setTextColor(tile.text_color) try: while 1: if not __debug__ and _.cardset is not None: raise AssertionError (id, random) = (_.nextgame.id, _.nextgame.random) (_.nextgame.id, _.nextgame.random) = (0, None) _.runGame(id, random) if _.nextgame.holdgame: if not __debug__ and _.nextgame.id <= 0: raise AssertionError try: _.game.gstats.holded = 1 _.game._saveGame(_.fn.holdgame) _.opt.game_holded = _.game.id except: pass _.freeGame() if _.nextgame.id <= 0: break if _.nextgame.cardset is not _.cardset: _.loadCardset(_.nextgame.cardset, id = _.nextgame.id, update = 7 + 256) else: _.requestCompatibleCardsetType(_.nextgame.id) finally: _.opt.last_gameid = id try: _.wm_save_state() except: pass try: _.saveOptions() except: pass try: _.saveStatistics() except: pass try: _.saveComments() except: pass try: _.audio.destroy() except: pass def runGame(_, id, random = None): _.top.connectApp(_) g = _.getGameClass(id) if g is None: id = 2 random = None g = _.getGameClass(id) if g is None: id = _.gdb.getGamesIdSortedByName()[0] g = _.getGameClass(id) gi = _.getGameInfo(id) if __debug__: if not g and type(g) is types.ClassType and id > 0: raise AssertionError if __debug__: if not gi is not None and gi.id == id: raise AssertionError _.game = _.constructGame(id) _.gdb.setSelected(id) _.game.busy = 1 _.game.create(_) if not (_.nextgame.startdemo) and not (_.nextgame.bookmark): if _.requestCompatibleCardsetSize() > 0: _.nextgame.id = id _.nextgame.random = random return None _.menubar.connectGame(_.game) _.toolbar.connectGame(_.game, _.menubar) _.game.updateStatus(player = _.opt.player) while 1: try: _.opt.recent_gameid.remove(id) except ValueError: break _.opt.recent_gameid.insert(0, id) del _.opt.recent_gameid[15:] _.menubar.updateRecentGamesMenu(_.opt.recent_gameid) if _.intro.progress: _.intro.progress.destroy() destruct(_.intro.progress) _.intro.progress = None autoplay = 0 if _.nextgame.loadedgame is not None: _.stats.gameid_balance = 0 _.game.restoreGame(_.nextgame.loadedgame) destruct(_.nextgame.loadedgame) elif _.nextgame.bookmark is not None: _.game.restoreGameFromBookmark(_.nextgame.bookmark) else: _.stats.gameid_balance = 0 _.game.newGame(random = random, autoplay = 0) autoplay = 1 _.nextgame.loadedgame = None _.nextgame.bookmark = None if _.debug or bundle & 4: pass elif _.splashscreen > 0: status = helpAbout(_, timeout = 20000, sound = 0) if status == 2: _.nextgame.startdemo = 1 _.splashscreen = 0 if _.nextgame.startdemo: _.nextgame.startdemo = 0 _.game.startDemo() _.game.createDemoInfoText() elif autoplay: _.game.autoPlay() _.game.stats.player_moves = 0 _.game.busy = 0 _.top.mainloop() def freeGame(_): _.toolbar.connectGame(None, None) _.menubar.connectGame(None) unbind_destroy(_.canvas) _.canvas.deleteAllItems() _.canvas.update_idletasks() if _.game: _.game.destruct() destruct(_.game) _.game = None _.top.connectApp(None) def dumpMem(_, info = ''): pass def wm_save_state(_): if _.top: s = _.top.wm_state() if s == 'zoomed': _.opt.wm_maximized = 1 elif s == 'normal': _.opt.wm_maximized = 0 def wm_withdraw(_): if _.intro.progress: _.intro.progress.destroy() destruct(_.intro.progress) _.intro.progress = None if _.top: wm_withdraw(_.top) _.top.busyUpdate() def loadImages1(_): dir = os.path.join('images', 'logos') _.gimages.logos.append(loadImage(_.dataloader.findImage('joker07_40_774', dir))) _.gimages.logos.append(loadImage(_.dataloader.findImage('joker08_40_774', dir))) _.gimages.logos.append(_.dataloader.findImage('joker07_50_774', dir)) _.gimages.logos.append(_.dataloader.findImage('joker08_50_774', dir)) _.gimages.logos.append(_.dataloader.findImage('joker11_100_774', dir)) _.gimages.logos.append(_.dataloader.findImage('joker10_100', dir)) _.gimages.logos.append(_.dataloader.findImage('pysol_40', dir)) dir = 'images' for f in ('stopsign', 'redeal'): _.gimages.redeal.append(_.dataloader.findImage(f, dir)) def loadImages2(_): dir = os.path.join('images', 'demo') for f in ('demo01', 'demo02', 'demo03', 'demo04', 'demo05'): _.gimages.demo.append(_.dataloader.findImage(f, dir)) dir = os.path.join('images', 'stats') for f in ('barchart',): _.gimages.stats.append(_.dataloader.findImage(f, dir)) def loadImages3(_): pass def loadImages4(_): for k, v in _.gimages.__dict__.items(): if type(v) is types.ListType: for i in range(len(v)): pass _.gimages.__dict__[k] = tuple(v) def getToolbarImagesDir(_, size): dir = 'small' if size: dir = 'large' dir = os.path.join(_.dataloader.dir, 'toolbar', dir) return dir def updateCardset(_, id = 0, update = 7): cs = _.images.cs _.cardset = cs _.nextgame.cardset = cs _.cardset_manager.setSelected(cs.index) if update & 1: _.opt.cardset[0] = (cs.name, cs.backname) if update & 2: _.opt.cardset[cs.si.type] = (cs.name, cs.backname) gi = _.getGameInfo(id) if gi: if update & 256: try: del _.opt.cardset[(1, gi.id)] except KeyError: pass t = _.checkCompatibleCardsetType(gi, cs) if not t[1]: if update & 4: _.opt.cardset[gi.category] = (cs.name, cs.backname) if update & 8: _.opt.cardset[(1, gi.id)] = (cs.name, cs.backname) def loadCardset(_, cs, id = 0, update = 7, progress = None): r = 0 if cs is None or cs.error: return 0 if cs is _.cardset: _.updateCardset(id, update = update) return 1 if progress is None: _.wm_save_state() _.wm_withdraw() title = 'Loading ' + CARDSET + ' ' + cs.name + '...' color = _.opt.tablecolor if _.tabletile_index > 0: color = '#008200' progress = PysolProgressBar(_, _.top, title = title, color = color, bg = _.progress_bg, images = _.progress_images) images = Images(_.dataloader, cs) try: if not images.load(app = _, progress = progress): raise Exception, 'Invalid or damaged ' + CARDSET simages = SubsampledImages(images) if _.images is not None: _.images.destruct() destruct(_.images) _.images = images _.subsampled_images = simages _.updateCardset(id, update = update) r = 1 except (Exception, TclError, UnpicklingError): ex = None cs.error = 1 _.nextgame.cardset = _.cardset if _.cardset: _.cardset_manager.setSelected(_.cardset.index) images.destruct() destruct(images) d = MfxExceptionDialog(_.top, ex, title = CARDSET + ' load error', text = 'Error while loading ' + CARDSET) _.intro.progress = progress if r and _.menubar is not None: _.menubar.updateBackgroundImagesMenu() return r def checkCompatibleCardsetType(_, gi, cs): if not __debug__ and gi is not None: raise AssertionError if not __debug__ and cs is not None: raise AssertionError gc = gi.category cs_type = cs.si.type (t0, t1) = (None, None) if gc == GI.GC_FRENCH: t0 = 'French' if cs_type not in (CSI.TYPE_FRENCH, CSI.TYPE_TAROCK): t1 = t0 elif gc == GI.GC_HANAFUDA: t0 = 'Hanafuda' if cs_type not in (CSI.TYPE_HANAFUDA,): t1 = t0 elif gc == GI.GC_TAROCK: t0 = 'Tarock' if cs_type not in (CSI.TYPE_TAROCK,): t1 = t0 elif gc == GI.GC_MAHJONGG: t0 = 'Mahjongg' if cs_type not in (CSI.TYPE_MAHJONGG,): t1 = t0 elif gc == GI.GC_HEXADECK: t0 = 'Hex A Deck' if cs_type not in (CSI.TYPE_HEXADECK,): t1 = t0 elif gc == GI.GC_MUGHAL_GANJIFA: t0 = 'Mughal Ganjifa' if cs_type not in (CSI.TYPE_MUGHAL_GANJIFA, CSI.TYPE_NAVAGRAHA_GANJIFA, CSI.TYPE_DASHAVATARA_GANJIFA): t1 = t0 elif gc == GI.GC_NAVAGRAHA_GANJIFA: t0 = 'Navagraha Ganjifa' if cs_type not in (CSI.TYPE_NAVAGRAHA_GANJIFA, CSI.TYPE_DASHAVATARA_GANJIFA): t1 = t0 elif gc == GI.GC_DASHAVATARA_GANJIFA: t0 = 'Dashavatara Ganjifa' if cs_type not in (CSI.TYPE_DASHAVATARA_GANJIFA,): t1 = t0 elif gc == GI.GC_TRUMP_ONLY: t0 = 'Trump only' if cs_type not in (CSI.TYPE_TRUMP_ONLY,): t1 = t0 elif len(cs.trumps) < gi.ncards: t1 = t0 else: t0 = t1 = 'Unknown' return (t0, t1) def getCompatibleCardset(_, gi, cs): if gi is None: return (cs, 1) if cs: t = _.checkCompatibleCardsetType(gi, cs) if not t[1]: return (cs, 1) for key, flag in (((1, gi.id), 8), (gi.category, 4)): c = _.opt.cardset.get(key) cs = _.cardset_manager.getByName(c[0]) if not cs: continue t = _.checkCompatibleCardsetType(gi, cs) if not t[1]: cs.updateCardback(backname = c[1]) return (cs, flag) return (None, 0) def requestCompatibleCardsetType(_, id): gi = _.getGameInfo(id) (cs, cs_update_flag) = _.getCompatibleCardset(gi, _.cardset) if cs is _.cardset: return 0 _.wm_save_state() _.wm_withdraw() if cs is not None: _.loadCardset(cs, update = 1) return 1 t = _.checkCompatibleCardsetType(gi, _.cardset) d = MfxDialog(_.top, title = 'Incompatible ' + CARDSET, bitmap = 'warning', text = 'The currently selected ' + CARDSET + '\n' + _.cardset.name + '\n\nis not compatible with the game\n' + gi.name + '\n\nPlease select a ' + t[0] + ' type ' + CARDSET + '.', strings = ('OK',), default = 0) cs = _._Application__selectCardsetDialog(t) if cs is None: return -1 _.loadCardset(cs, id = id) return 1 def requestCompatibleCardsetSize(_): (game, gi) = (_.game, _.game.gameinfo) (sw, sh) = (_.top.winfo_screenwidth(), _.top.winfo_screenheight()) if sw - game.width >= 0 and sh - game.height >= 32: return 0 (cs, cs_update_flag) = _.getCompatibleCardset(gi, None) if cs is _.cardset: if cs_update_flag & 8: return 0 cs = None _.wm_save_state() _.wm_withdraw() if cs is not None: _.loadCardset(cs, update = 1) return 1 t = _.checkCompatibleCardsetType(gi, _.cardset) d = MfxDialog(_.top, title = 'Incompatible ' + CARDSET, bitmap = 'warning', text = 'The currently selected ' + CARDSET + '\n' + _.cardset.name + '\n\nis too large for the game\n' + gi.name + '\n\nPlease increase your screen resolution\nor select a smaller ' + CARDSET + '.', strings = ('Select ' + CARDSET + '...', 'Continue'), default = 0) if d.status != 0: return -1 if d.button == 1: _.updateCardset(_.game.id, update = 8) return 0 cs = _._Application__selectCardsetDialog(t) if cs is None: return -1 _.loadCardset(cs, id = _.game.id, update = 9) return 1 def __selectCardsetDialog(_, t): key = _.cardset.index d = SelectCardsetByTypeDialogWithPreview(_.top, title = 'Please select a ' + t[0] + ' type ' + CARDSET, app = _, manager = _.cardset_manager, key = key, strings = (None, 'OK', 'Cancel'), default = 1) if d.status != 0 or d.button != 1: return None cs = _.cardset_manager.get(d.key) if cs is None or d.key == key: return None return cs def loadOptions(_): _.opt.setDefaults(_.top) opt = unpickle(_.fn.opt) if opt: cardset = _.opt.cardset if hasattr(opt, 'version_tuple') and hasattr(opt, 'cardset'): merge_dict(cardset, opt.cardset) merge_dict(_.opt.__dict__, opt.__dict__) _.opt.cardset = cardset _.opt.setConstants() def loadStatistics(_): stats = unpickle(_.fn.stats) if stats: merge_dict(_.stats.__dict__, stats.__dict__) _.stats.session_games = { } _.stats.session_balance = { } _.stats.gameid_balance = 0 def loadComments(_): comments = unpickle(_.fn.comments) if comments: merge_dict(_.comments.__dict__, comments.__dict__) def __saveObject(_, obj, fn): obj.version_tuple = VERSION_TUPLE obj.saved = obj.saved + 1 pickle(obj, fn, binmode = 1) def saveOptions(_): _._Application__saveObject(_.opt, _.fn.opt) def saveStatistics(_): _._Application__saveObject(_.stats, _.fn.stats) def saveComments(_): _._Application__saveObject(_.comments, _.fn.comments) def constructGame(_, id): gi = _.gdb.get(id) if gi is None: raise Exception, 'Unknown game (id %d)' % id return gi.gameclass(gi) def getGamesIdSortedById(_): return _.gdb.getGamesIdSortedById() def getGamesIdSortedByName(_): return _.gdb.getGamesIdSortedByName() def getGameInfo(_, id): return _.gdb.get(id) def getGameClass(_, id): gi = _.gdb.get(id) if gi is None: return None return gi.gameclass def getGameTitleName(_, id): gi = _.gdb.get(id) if gi is None: return None return gi.name def getGameMenuitemName(_, id): gi = _.gdb.get(id) if gi is None: return None return gi.short_name def getGameRulesFilename(_, id): gi = _.gdb.get(id) if gi is None: return None if gi.rules_filename is not None: return gi.rules_filename n = gi.name n = re.sub('[\\[\\(].*$', '', n) n = latin1_to_ascii(n) n = re.sub('[^\\w]', '', n) n = string.lower(n) + '.html' gi.rules_filename = n return n def getGameSaveName(_, id): n = _.getGameTitleName(id) if not n: return None m = re.search('^(.*)([\\[\\(](\\w+).*[\\]\\)])\\s*$', n) if m: n = m.group(1) + '_' + string.lower(m.group(2)) n = latin1_to_ascii(n) return re.sub('[^\\w\\-]', '', n) def getRandomGameId(_): return _.miscrandom.choice(_.gdb.getGamesIdSortedById()) def loadPlugins(_, dir): if not dir and not os.path.isdir(dir) or bundle & 4: return None names = os.listdir(dir) names = map(os.path.normcase, names) names.sort() for name in names: m = re.search('^(.+)\\.py$', name) n = os.path.join(dir, name) def _readCardsetConfig(_, dir, filename): f = None try: f = open(filename, 'r') lines = f.readlines() finally: if f: f.close() lines = map(string.strip, lines) if string.index(lines[0], 'PySol') != 0: return None config = CardsetConfig() if not _._parseCardsetConfig(config, lines): return None if config.CARDD > _.top.winfo_screendepth(): return None cs = Cardset() cs.dir = dir cs.update(config.__dict__) return cs def _parseCardsetConfig(_, cs, line): if len(line) < 6: return 0 fields = filter(None, re.split(';', line[0])) fields = map(string.strip, fields) if len(fields) >= 2: m = re.search('^(\\d+)$', fields[1]) if m: cs.version = int(m.group(1)) if cs.version >= 3: if len(fields) < 5: return 0 cs.ext = fields[2] m = re.search('^(\\d+)$', fields[3]) if not m: return 0 cs.type = int(m.group(1)) m = re.search('^(\\d+)$', fields[4]) if not m: return 0 cs.ncards = int(m.group(1)) if cs.version >= 4: if len(fields) < 6: return 0 styles = string.splitfields(fields[5], ',') for s in styles: m = re.search('^\\s*(\\d+)\\s*$', s) s = int(m.group(1)) if not (s in cs.styles): cs.styles.append(s) if cs.version >= 5: if len(fields) < 7: return 0 m = re.search('^(\\d+)$', fields[6]) if not m: return 0 cs.year = int(m.group(1)) if len(cs.ext) < 2 or cs.ext[0] != '.': return 0 if not line[1]: return 0 cs.ident = line[1] m = re.search('^(.*;)?([^;]+)$', cs.ident) if not m: return 0 cs.name = string.strip(m.group(2)) m = re.search('^(\\d+)\\s+(\\d+)\\s+(\\d+)', line[2]) if not m: return 0 (cs.CARDW, cs.CARDH, cs.CARDD) = (int(m.group(1)), int(m.group(2)), int(m.group(3))) m = re.search('^(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)', line[3]) if not m: return 0 cs.CARD_UP_YOFFSET = int(m.group(1)) cs.SHADOW_XOFFSET = int(m.group(3)) cs.SHADOW_YOFFSET = int(m.group(4)) back = line[4] if not back: return 0 cs.backnames = re.split(';', line[5]) cs.backnames = map(string.strip, cs.backnames) cs.backnames = filter(None, cs.backnames) if back in cs.backnames: cs.backindex = cs.backnames.index(back) else: cs.backnames.insert(0, back) cs.backindex = 0 return 1 def initCardsets(_): manager = _.cardset_manager dirs = manager.getSearchDirs(_, ('cardsets', ''), 'PYSOL_CARDSETS') if _.debug: dirs = dirs + manager.getSearchDirs(_, 'cardsets-*') try: dirs = dirs + manager.getRegistryDirs(_, ('PySol_Cardsets', 'Cardsets')) except: pass (found, t) = ([], { }) for dir in dirs: dir = string.strip(dir) try: names = [] for name in names: m = re.search('^cardset-', name, re.I) d = os.path.join(dir, name) if not os.path.isdir(d): continue f1 = os.path.join(d, 'config.txt') f2 = os.path.join(d, 'COPYRIGHT') if os.path.isfile(f1) and os.path.isfile(f2): try: cs = _._readCardsetConfig(d, f1) if cs: back = cs.backnames[cs.backindex] f1 = os.path.join(d, back) f2 = os.path.join(d, 'shade' + cs.ext) if os.path.isfile(f1) and os.path.isfile(f2): found.append(cs) except: pass except EnvError: 0 ex = 0 dirs except: 0 for obj in found: pass def initTiles(_): manager = _.tabletile_manager dirs = manager.getSearchDirs(_, 'tiles-*', 'PYSOL_TILES') try: dirs = dirs + manager.getRegistryDirs(_, 'Tiles') except: pass s = '((\\' + string.join(IMAGE_EXTENSIONS, ')|(\\') + '))$' ext_re = re.compile(s, re.I) text_color_re = re.compile('^(.+)-([0-9A-Fa-f]{6})$') (found, t) = ([], { }) for dir in dirs: dir = string.strip(dir) try: names = [] for name in names: f = os.path.join(dir, name) if not os.path.isfile(f): continue tile = Tile() tile.filename = f n = ext_re.sub('', string.strip(name)) m = text_color_re.search(n) if m: n = m.group(1) tile.text_color = '#' + string.lower(m.group(2)) n = re.sub('_', ' ', n) tile.name = n key = string.lower(n) if not t.has_key(key): t[key] = 1 found.append((n, tile)) except EnvError: 0 ex = 0 dirs except: 0 found.sort() for f in found: obj = f[1] def initResource(_, manager, dirs, ext_re, Resource_Class): (found, t) = ([], { }) for dir in dirs: dir = string.strip(dir) try: names = [] if dir and os.path.isdir(dir): names = os.listdir(dir) names = map(os.path.normcase, names) names.sort() for name in names: f = os.path.join(dir, name) f = os.path.normpath(f) if not os.path.isfile(f): continue obj = Resource_Class() obj.filename = f n = ext_re.sub('', string.strip(name)) obj.name = n key = string.lower(n) if not t.has_key(key): t[key] = 1 found.append((n, obj)) except EnvError: None if dir else dirs ex = None if dir else dirs except: None if dir else dirs found.sort() if manager: for f in found: obj = f[1] return found def initSamples(_): manager = _.sample_manager dirs = manager.getSearchDirs(_, ('sound', os.path.join('sound', 'extra'))) ext_re = re.compile('\\.((wav))$', re.I) _.initResource(manager, dirs, ext_re, Sample) def initMusic(_): manager = _.music_manager dirs = manager.getSearchDirs(_, 'music-*', 'PYSOL_MUSIC') try: dirs = dirs + manager.getRegistryDirs(_, 'Music') except: pass ext_re = re.compile('\\.((it)|(mod)|(mp3)|(pym)|(s3m)|(xm))$', re.I) _.initResource(manager, dirs, ext_re, Music) def fatal_no_cardsets(app): app.wm_withdraw() d = MfxDialog(app.top, title = PACKAGE + ' installation error', text = 'No ' + CARDSET + 's were found !!!\n\nMain data directory is:\n' + app.dataloader.dir + '\n\nPlease check your ' + PACKAGE + ' installation.', bitmap = 'error', strings = ('Quit',)) def pysol_init(app, args): try: os.makedirs(app.dn.config, 488) except: pass try: os.mkdir(os.path.join(app.dn.savegames), 488) except: pass try: os.mkdir(os.path.join(app.dn.config, 'music'), 488) except: pass try: os.mkdir(os.path.join(app.dn.config, 'screenshots'), 488) except: pass try: os.mkdir(os.path.join(app.dn.config, 'tiles'), 488) except: pass f = os.path.join('html', 'license.html') app.dataloader = DataLoader(args[0], f) wm_command = '' opt_nosound = 0 prog = sys.executable argv0 = os.path.normpath(args[0]) if prog and os.path.isfile(prog): prog = os.path.abspath(prog) if os.path.isfile(argv0): wm_command = prog + ' ' + os.path.abspath(argv0) for a in args[1:]: if os.path.isfile(a): app.commandline.loadgame = a elif a == '--nodebug': app.debug = 0 elif a == '--debug': app.debug = app.debug + 1 elif a[:9] == '--bindir=': wm_command = wm_command + ' ' + a elif a == '--bundle': bundle = bundle + 1 elif a == '--nosound': opt_nosound = 1 wm_command = wm_command + ' ' + a top = MfxRoot(className = PACKAGE) app.top = top app.top_cursor = top.cget('cursor') try: app.loadOptions() except EnvError: ex = None except: pass if not (app.debug): app.dn.maint = None if 1 and app.debug >= 2: print 'prog:', prog print 'argv0:', argv0 print 'wm_command:', wm_command print 'sys.executable:', sys.executable print 'sys.version:', sys.version print 'sys.platform:', sys.platform print 'sys.argv:', sys.argv print 'sys.path:', sys.path print 'dataloader:', app.dataloader.__dict__ print 'tkname:', tkname print 'tkversion:', tkversion if 1 and app.debug >= 2: GI().assertGI(app.gdb) app.audio = None if not opt_nosound: if os.name == 'nt' and app.opt.sound_mode == 0: app.audio = Win32AudioClient() elif pysolsoundserver: app.audio = PysolSoundServerModuleClient() elif os.name == 'nt': app.audio = Win32AudioClient() if app.audio: app.audio.startServer() if app.audio.server is None: if os.name == 'nt' and not isinstance(app.audio, Win32AudioClient): app.audio.destroy() app.audio = Win32AudioClient() app.audio.startServer() else: app.audio = AbstractAudioClient() if isinstance(app.audio, PysolSoundServerModuleClient): app.opt.sound_mode = 1 else: app.opt.sound_mode = 0 top.wm_group(top) top.wm_title(PACKAGE + ' ' + VERSION) top.wm_iconname(PACKAGE + ' ' + VERSION) top.wm_minsize(200, 200) top.wm_protocol('WM_DELETE_WINDOW', top.wmDeleteWindow) if wm_command: top.wm_command(wm_command) (sw, sh, sd) = (top.winfo_screenwidth(), top.winfo_screenheight(), top.winfo_screendepth()) if sw < 640 and sh < 480 or sd < 8: app.wm_withdraw() d = MfxDialog(top, title = PACKAGE + ' init error', text = PACKAGE + ' requires a minimum screen resolution\nof 640x480 in 256 colors.', bitmap = 'error', strings = ('Quit',)) return 1 if 1: top.config(width = min(800, sw - 64), height = min(600, sh - 64), bg = app.opt.tablecolor) else: top.config(bg = app.opt.tablecolor) try: wm_set_icon(top, app.dataloader.findIcon()) except: pass if os.name == 'posix': color = '#d9d9d9' top.tk_setPalette('background', color, 'activeBackground', color) if len(app.gdb.getGamesIdSortedByName()) == 0: app.wm_withdraw() d = MfxDialog(top, title = PACKAGE + ' installation error', text = 'No games were found !!!\n\nMain data directory is:\n' + app.dataloader.dir + '\n\nPlease check your ' + PACKAGE + ' installation.', bitmap = 'error', strings = ('Quit',)) return 1 app.initCardsets() cardset = None c = app.opt.cardset.get(0) if c: cardset = app.cardset_manager.getByName(c[0]) if cardset and c[1]: cardset.updateCardback(backname = c[1]) if not cardset: cardset = app.cardset_manager.get(0) if app.cardset_manager.len() == 0 or not cardset: fatal_no_cardsets(app) return 3 manager = app.tabletile_manager tile = Tile() tile.name = 'None' tile.filename = None manager.register(tile) app.initTiles() if app.opt.tabletile_name: for tile in manager.getAll(): pass manager.setSelected(app.tabletile_index) app.initSamples() app.initMusic() app.audio.connectServer(app) if app.audio.audiodev is None: app.opt.sound = 0 app.audio.updateSettings() if app.audio.audiodev: music = app.music_manager.getAll() if music: app.music_playlist = list(music)[:] app.miscrandom.shuffle(app.music_playlist) if 1: for m in app.music_playlist: pass app.audio.playContinuousMusic(app.music_playlist) app.progress_bg = '#c0c0c0' app.loadImages1() if not (app.progress_images): app.progress_images = (app.gimages.logos[0], app.gimages.logos[1]) app.wm_withdraw() warn_thread = 0 warn_pysolsoundserver = 0 if not opt_nosound and os.name == 'posix' and pysolsoundserver is None: if 1 and app.opt.sound and re.search('linux', sys.platform, re.I): warn_pysolsoundserver = 1 if thread is None: warn_thread = 1 if thread is None: print PACKAGE + ': Python thread module not found, sound disabled.' else: print PACKAGE + ': pysolsoundserver module not found, sound disabled.' sys.stdout.flush() if warn_thread: top.update() d = MfxDialog(top, title = PACKAGE + ' installation problem', text = 'Your Python installation is compiled without thread support.\n\nSounds and background music will be disabled.', bitmap = 'warning', strings = ('OK',)) elif warn_pysolsoundserver: top.update() d = MfxDialog(top, title = PACKAGE + ' installation problem', text = 'The pysolsoundserver module was not found.\n\nSounds and background music will be disabled.', bitmap = 'warning', strings = ('OK',)) title = 'Welcome to ' + PACKAGE color = app.opt.tablecolor if app.tabletile_index > 0: color = '#008200' app.intro.progress = PysolProgressBar(app, top, title = title, color = color, bg = app.progress_bg, images = app.progress_images) app.loadImages2() app.loadImages3() app.loadImages4() progress = app.intro.progress if not app.loadCardset(cardset, progress = progress, update = 1): for cardset in app.cardset_manager.getAll(): progress.reset() else: return 3 return 0 def pysol_exit(app): if app.audio is not None: app.audio.destroy() destruct(app.audio) app.wm_withdraw() if app.canvas is not None: app.canvas.destroy() destruct(app.canvas) if app.toolbar is not None: app.toolbar.destroy() destruct(app.toolbar) if app.menubar is not None: destruct(app.menubar) top = app.top destruct(app) app = None if top is not None: try: top.destroy() except: pass destruct(top) def pysol_main(args): app = Application() try: r = pysol_init(app, args) if r != 0: return r app.mainloop() except KeyboardInterrupt: ex = None print 'Exiting on SIGINT.' except StandardError: ex = None if not (app.top): raise t = str(ex.__class__) if str(ex): t = t + ':\n' + str(ex) d = MfxDialog(app.top, title = PACKAGE + ' internal error', text = 'Internal errror. Please report this bug:\n\n' + t, strings = ('Quit',), bitmap = 'error') try: pysol_exit(app) except: pass return 0 def main(args = None): if not hasattr(sys, 'platform'): sys.platform = 'unknown' if not hasattr(sys, 'executable'): sys.executable = None if not hasattr(os, 'defpath'): os.defpath = '' if sys.platform[:4] != 'java': if sys.version[:5] < '1.5.2': print '%s needs Python 1.5.2 or better (you have %s)' % (PACKAGE, sys.version) return 1 if not __debug__ and len(tkversion) == 4: raise AssertionError if tkname == 'tk': if tkversion < (8, 0, 0, 0): print '%s needs Tcl/Tk 8.0 or better (you have %s)' % (PACKAGE, str(tkversion)) return 1 if not hasattr(Tkinter.Wm, 'wm_aspect') or not hasattr(Tkinter.Canvas, 'tag_lower'): print '%s: please update the Python-Tk bindings (aka Tkinter) to version 1.5.2 or better' % (PACKAGE,) return 1 if -1 % 13 != 12: raise Exception, '-1 % 13 != 12' r = pysol_main(args) return r sys.exit(main(sys.argv))